Unstable v0.15.17.0 (Hex is out of town edition)

This commit is contained in:
Juan Pablo Arce
2021-12-03 13:31:10 -03:00
parent cd5c8f3e13
commit 617d9ede88
245 changed files with 8088 additions and 5842 deletions

View File

@@ -63,7 +63,10 @@ namespace Barotrauma
{ {
foreach (var ic in character.MemState[0].SelectedItem.Components) foreach (var ic in character.MemState[0].SelectedItem.Components)
{ {
if (ic.CanBeSelected) ic.Select(character); if (ic.CanBeSelected)
{
ic.Select(character);
}
} }
} }
character.SelectedConstruction = character.MemState[0].SelectedItem; character.SelectedConstruction = character.MemState[0].SelectedItem;
@@ -98,6 +101,16 @@ namespace Barotrauma
if (distSqrd > 10.0f || !character.CanMove) if (distSqrd > 10.0f || !character.CanMove)
{ {
Collider.TargetRotation = newRotation; Collider.TargetRotation = newRotation;
if (distSqrd > 10.0f)
{
//teleported very far - see if we need to move to another sub
Hull serverHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(newPosition), CurrentHull, newPosition.Y < lowestSubPos);
if (currentHull != null && serverHull != null && serverHull.Submarine != currentHull.Submarine)
{
character.Submarine = serverHull.Submarine;
character.CurrentHull = CurrentHull = serverHull;
}
}
SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false); SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false);
} }
else else
@@ -194,7 +207,7 @@ namespace Barotrauma
{ {
if (character.SelectedConstruction != serverPos.SelectedItem) if (character.SelectedConstruction != serverPos.SelectedItem)
{ {
serverPos.SelectedItem.TryInteract(character, true, true); serverPos.SelectedItem.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true);
} }
character.SelectedConstruction = serverPos.SelectedItem; character.SelectedConstruction = serverPos.SelectedItem;
} }
@@ -448,8 +461,8 @@ namespace Barotrauma
{ {
DebugConsole.ThrowError("Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace.CleanupStackTrace()); DebugConsole.ThrowError("Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace.CleanupStackTrace());
GameAnalyticsManager.AddErrorEventOnce("Ragdoll.Draw:LimbsRemoved", GameAnalyticsManager.AddErrorEventOnce("Ragdoll.Draw:LimbsRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
"Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace.CleanupStackTrace()); "Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.SpeciesName + "\", removed: " + character.Removed + "\n" + Environment.StackTrace.CleanupStackTrace());
return; return;
} }
@@ -460,12 +473,17 @@ namespace Barotrauma
} }
float depthOffset = GetDepthOffset(); float depthOffset = GetDepthOffset();
if (!MathUtils.NearlyEqual(depthOffset, 0.0f))
{
foreach (Limb limb in limbs) { limb.ActiveSprite.Depth += depthOffset; }
}
for (int i = 0; i < limbs.Length; i++) for (int i = 0; i < limbs.Length; i++)
{ {
var limb = inversedLimbDrawOrder[i]; inversedLimbDrawOrder[i].Draw(spriteBatch, cam, color);
if (depthOffset != 0.0f) { limb.ActiveSprite.Depth += depthOffset; } }
limb.Draw(spriteBatch, cam, color); if (!MathUtils.NearlyEqual(depthOffset, 0.0f))
if (depthOffset != 0.0f) { limb.ActiveSprite.Depth -= depthOffset; } {
foreach (Limb limb in limbs) { limb.ActiveSprite.Depth -= depthOffset; }
} }
LimbJoints.ForEach(j => j.Draw(spriteBatch)); LimbJoints.ForEach(j => j.Draw(spriteBatch));
} }
@@ -486,7 +504,14 @@ namespace Barotrauma
if (character.WorldPosition.X < character.SelectedConstruction.WorldPosition.X) if (character.WorldPosition.X < character.SelectedConstruction.WorldPosition.X)
{ {
//at the left side of the ladder, needs to be drawn in front of the rungs //at the left side of the ladder, needs to be drawn in front of the rungs
depthOffset = Math.Max(ladder.BackgroundSpriteDepth - 0.01f - maxDepth, 0.0f); if (maxDepth > ladder.BackgroundSpriteDepth)
{
depthOffset = Math.Max(ladder.BackgroundSpriteDepth - 0.01f - maxDepth, 0.0f);
}
else
{
depthOffset = Math.Max(ladder.Item.GetDrawDepth() + 0.0001f - minDepth, -minDepth);
}
} }
else else
{ {

View File

@@ -305,9 +305,9 @@ namespace Barotrauma
case 0: //NetEntityEvent.Type.InventoryState case 0: //NetEntityEvent.Type.InventoryState
if (Inventory == null) if (Inventory == null)
{ {
string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")"; string errorMsg = "Received an inventory update message for an entity with no inventory ([name], removed: " + Removed + ")";
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg.Replace("[name]", Name));
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", SpeciesName));
//read anyway to prevent messing up reading the rest of the message //read anyway to prevent messing up reading the rest of the message
_ = msg.ReadUInt16(); _ = msg.ReadUInt16();
@@ -651,7 +651,7 @@ namespace Barotrauma
{ {
string errorMsg = $"Error in CharacterNetworking.ReadStatus: affliction not found ({afflictionName})"; string errorMsg = $"Error in CharacterNetworking.ReadStatus: affliction not found ({afflictionName})";
causeOfDeathType = CauseOfDeathType.Unknown; causeOfDeathType = CauseOfDeathType.Unknown;
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionIndexOutOfBounts", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
} }
else else
{ {
@@ -682,7 +682,7 @@ namespace Barotrauma
if (severedJointIndex < 0 || severedJointIndex >= AnimController.LimbJoints.Length) 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})"; 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); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:JointIndexOutOfBounts", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
} }
else else
{ {

View File

@@ -453,13 +453,14 @@ namespace Barotrauma
{ {
case Alignment.Left: case Alignment.Left:
healthWindow.RectTransform.SetPosition(Anchor.BottomLeft); healthWindow.RectTransform.SetPosition(Anchor.BottomLeft);
healthWindow.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.InventoryAreaLower.X, screenResolution.Y - HUDLayoutSettings.ChatBoxArea.Y + HUDLayoutSettings.Padding);
break; break;
case Alignment.Right: case Alignment.Right:
healthWindow.RectTransform.SetPosition(Anchor.BottomRight); healthWindow.RectTransform.SetPosition(Anchor.BottomRight);
healthWindow.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.Padding, screenResolution.Y - HUDLayoutSettings.ChatBoxArea.Y + HUDLayoutSettings.Padding);
break; break;
} }
healthWindow.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.Padding, screenResolution.Y - HUDLayoutSettings.ChatBoxArea.Y + HUDLayoutSettings.Padding);
healthWindow.RectTransform.RecalculateChildren(false); healthWindow.RectTransform.RecalculateChildren(false);
} }
@@ -648,8 +649,9 @@ namespace Barotrauma
grainColor = oxygenLowGrainColor; grainColor = oxygenLowGrainColor;
} }
foreach (Affliction affliction in afflictions) foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
{ {
var affliction = kvp.Key;
distortStrength = Math.Max(distortStrength, affliction.GetScreenDistortStrength()); distortStrength = Math.Max(distortStrength, affliction.GetScreenDistortStrength());
blurStrength = Math.Max(blurStrength, affliction.GetScreenBlurStrength()); blurStrength = Math.Max(blurStrength, affliction.GetScreenBlurStrength());
radialDistortStrength = Math.Max(radialDistortStrength, affliction.GetRadialDistortStrength()); radialDistortStrength = Math.Max(radialDistortStrength, affliction.GetRadialDistortStrength());
@@ -662,16 +664,6 @@ namespace Barotrauma
grainColor = Color.Lerp(grainColor, afflictionGrainColor, (float)Math.Pow(1.0f - oxygenLowStrength, 2)); grainColor = Color.Lerp(grainColor, afflictionGrainColor, (float)Math.Pow(1.0f - oxygenLowStrength, 2));
} }
} }
foreach (LimbHealth limbHealth in limbHealths)
{
foreach (Affliction affliction in limbHealth.Afflictions)
{
distortStrength = Math.Max(distortStrength, affliction.GetScreenDistortStrength());
blurStrength = Math.Max(blurStrength, affliction.GetScreenBlurStrength());
radialDistortStrength = Math.Max(radialDistortStrength, affliction.GetRadialDistortStrength());
chromaticAberrationStrength = Math.Max(chromaticAberrationStrength, affliction.GetChromaticAberrationStrength());
}
}
Character.RadialDistortStrength = radialDistortStrength; Character.RadialDistortStrength = radialDistortStrength;
Character.ChromaticAberrationStrength = chromaticAberrationStrength; Character.ChromaticAberrationStrength = chromaticAberrationStrength;
@@ -777,7 +769,7 @@ namespace Barotrauma
{ {
// If no limb is selected or highlighted, select the one with the most critical afflictions. // If no limb is selected or highlighted, select the one with the most critical afflictions.
var affliction = SortAfflictionsBySeverity(GetAllAfflictions(a => a.Prefab.IndicatorLimb != LimbType.None)).FirstOrDefault(); var affliction = SortAfflictionsBySeverity(GetAllAfflictions(a => a.Prefab.IndicatorLimb != LimbType.None)).FirstOrDefault();
if (affliction.DamagePerSecond > 0 || affliction.Strength > 0) if (affliction != null && (affliction.DamagePerSecond > 0 || affliction.Strength > 0))
{ {
var limbHealth = GetMatchingLimbHealth(affliction); var limbHealth = GetMatchingLimbHealth(affliction);
if (limbHealth != null) if (limbHealth != null)
@@ -788,7 +780,7 @@ namespace Barotrauma
else else
{ {
// If no affliction is critical, select the limb which has most damage. // If no affliction is critical, select the limb which has most damage.
var limbHealth = limbHealths.OrderByDescending(l => l.TotalDamage).FirstOrDefault(); var limbHealth = limbHealths.OrderByDescending(l => GetTotalDamage(l)).FirstOrDefault();
selectedLimbIndex = limbHealths.IndexOf(limbHealth); selectedLimbIndex = limbHealths.IndexOf(limbHealth);
} }
} }
@@ -971,8 +963,9 @@ namespace Barotrauma
UpdateAlignment(); UpdateAlignment();
} }
foreach (Affliction affliction in afflictions) foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
{ {
var affliction = kvp.Key;
if (affliction.Prefab.AfflictionOverlay != null) if (affliction.Prefab.AfflictionOverlay != null)
{ {
Sprite ScreenAfflictionOverlay = affliction.Prefab.AfflictionOverlay; Sprite ScreenAfflictionOverlay = affliction.Prefab.AfflictionOverlay;
@@ -984,7 +977,7 @@ namespace Barotrauma
float damageOverlayAlpha = DamageOverlayTimer; float damageOverlayAlpha = DamageOverlayTimer;
if (Vitality < MaxVitality * 0.1f) if (Vitality < MaxVitality * 0.1f)
{ {
damageOverlayAlpha = Math.Max(1.0f - (Vitality / maxVitality * 10.0f), damageOverlayAlpha); damageOverlayAlpha = Math.Max(1.0f - (Vitality / UnmodifiedMaxVitality * 10.0f), damageOverlayAlpha);
} }
else else
{ {
@@ -1159,18 +1152,34 @@ namespace Barotrauma
afflictionIconContainer.Content.ClearChildren(); afflictionIconContainer.Content.ClearChildren();
return; return;
} }
var currentAfflictions = GetMatchingAfflictions(selectedLimb, a => a.ShouldShowIcon(Character));
if (currentAfflictions.Any(a => !displayedAfflictions.Any(d => d.affliction == a)) || if (afflictionsDirty())
displayedAfflictions.Any(a => !currentAfflictions.Contains(a.affliction)))
{ {
var currentAfflictions = afflictions.Where(a => a.Value == selectedLimb && a.Key.ShouldShowIcon(Character)).Select(a => a.Key);
CreateAfflictionInfos(currentAfflictions); CreateAfflictionInfos(currentAfflictions);
CreateRecommendedTreatments(); CreateRecommendedTreatments();
} }
//update recommended treatments if the strength of some displayed affliction has changed by > 1 //update recommended treatments if the strength of some displayed affliction has changed by > 1
else if (displayedAfflictions.Any(d => Math.Abs(d.strength - currentAfflictions.First(a => a == d.affliction).Strength) > 1.0f)) else if (displayedAfflictions.Any(d => Math.Abs(d.strength - d.affliction.Strength) > 1.0f))
{ {
CreateRecommendedTreatments(); CreateRecommendedTreatments();
} }
bool afflictionsDirty()
{
//not displaying one of the current afflictions -> dirty
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
{
if (kvp.Value != selectedLimb || !kvp.Key.ShouldShowIcon(Character)) { continue; }
if (!displayedAfflictions.Any(d => d.affliction == kvp.Key)) { return true; }
}
//displaying an affliction we no longer have -> dirty
foreach ((Affliction affliction, float strength) in displayedAfflictions)
{
if (!afflictions.Any(a => a.Key == affliction)) { return true; }
}
return false;
}
} }
private void CreateAfflictionInfos(IEnumerable<Affliction> afflictions) private void CreateAfflictionInfos(IEnumerable<Affliction> afflictions)
@@ -1604,7 +1613,7 @@ namespace Barotrauma
int i = 0; int i = 0;
foreach (LimbHealth limbHealth in limbHealths) foreach (LimbHealth limbHealth in limbHealths)
{ {
if (limbHealth.IndicatorSprite == null) continue; if (limbHealth.IndicatorSprite == null) { continue; }
float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height);
@@ -1623,6 +1632,7 @@ namespace Barotrauma
} }
} }
private static readonly List<Affliction> afflictionsDisplayedOnLimb = new List<Affliction>();
private void DrawHealthWindow(SpriteBatch spriteBatch, Rectangle drawArea, bool allowHighlight) private void DrawHealthWindow(SpriteBatch spriteBatch, Rectangle drawArea, bool allowHighlight)
{ {
if (Character.Removed) { return; } if (Character.Removed) { return; }
@@ -1633,21 +1643,32 @@ namespace Barotrauma
int i = 0; int i = 0;
foreach (LimbHealth limbHealth in limbHealths) foreach (LimbHealth limbHealth in limbHealths)
{ {
if (limbHealth.IndicatorSprite == null) continue; if (limbHealth.IndicatorSprite == null) { continue; }
Rectangle limbEffectiveArea = new Rectangle(limbHealth.IndicatorSprite.SourceRect.X + limbHealth.HighlightArea.X, Rectangle limbEffectiveArea = new Rectangle(limbHealth.IndicatorSprite.SourceRect.X + limbHealth.HighlightArea.X,
limbHealth.IndicatorSprite.SourceRect.Y + limbHealth.HighlightArea.Y, limbHealth.IndicatorSprite.SourceRect.Y + limbHealth.HighlightArea.Y,
limbHealth.HighlightArea.Width, limbHealth.HighlightArea.Width,
limbHealth.HighlightArea.Height); limbHealth.HighlightArea.Height);
float damageLerp = limbHealth.TotalDamage > 0.0f ? MathHelper.Lerp(0.2f, 1.0f, limbHealth.TotalDamage / 100.0f) : 0.0f; float totalDamage = GetTotalDamage(limbHealth);
var tempAfflictions = GetMatchingAfflictions(limbHealth, a => true); float damageLerp = totalDamage > 0.0f ? MathHelper.Lerp(0.2f, 1.0f, totalDamage / 100.0f) : 0.0f;
float negativeEffect = tempAfflictions.Where(a => !a.Prefab.IsBuff && a.ShouldShowIcon(Character)).Sum(a => a.Strength); float negativeEffect = 0.0f, positiveEffect = 0.0f;
//float negativeMaxEffect = tempAfflictions.Where(a => !a.Prefab.IsBuff).Sum(a => a.Prefab.MaxStrength); foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
float positiveEffect = tempAfflictions.Where(a => a.Prefab.IsBuff && a.ShouldShowIcon(Character)).Sum(a => a.Strength * 0.2f); {
//float positiveMaxEffect = tempAfflictions.Where(a => a.Prefab.IsBuff).Sum(a => a.Prefab.MaxStrength); if (kvp.Value != limbHealth) { continue; }
var affliction = kvp.Key;
if (!affliction.ShouldShowIcon(Character)) { continue; }
if (!affliction.Prefab.IsBuff)
{
negativeEffect += affliction.Strength;
}
else
{
positiveEffect += affliction.Strength * 0.2f;
}
}
float midPoint = (float)limbEffectiveArea.Center.Y / (float)limbHealth.IndicatorSprite.Texture.Height; float midPoint = (float)limbEffectiveArea.Center.Y / (float)limbHealth.IndicatorSprite.Texture.Height;
float fadeDist = 0.6f * (float)limbEffectiveArea.Height / (float)limbHealth.IndicatorSprite.Texture.Height; float fadeDist = 0.6f * (float)limbEffectiveArea.Height / (float)limbHealth.IndicatorSprite.Texture.Height;
@@ -1714,7 +1735,7 @@ namespace Barotrauma
drawArea.Width / (float)limbIndicatorOverlay.FrameSize.X, drawArea.Width / (float)limbIndicatorOverlay.FrameSize.X,
drawArea.Height / (float)limbIndicatorOverlay.FrameSize.Y); drawArea.Height / (float)limbIndicatorOverlay.FrameSize.Y);
int frame = 0; int frame;
int frameCount = 17; int frameCount = 17;
if (limbIndicatorOverlayAnimState >= frameCount * 2) limbIndicatorOverlayAnimState = 0.0f; if (limbIndicatorOverlayAnimState >= frameCount * 2) limbIndicatorOverlayAnimState = 0.0f;
if (limbIndicatorOverlayAnimState < frameCount) if (limbIndicatorOverlayAnimState < frameCount)
@@ -1758,14 +1779,28 @@ namespace Barotrauma
i = 0; i = 0;
foreach (LimbHealth limbHealth in limbHealths) foreach (LimbHealth limbHealth in limbHealths)
{ {
IEnumerable<Affliction> thisAfflictions = limbHealth.Afflictions.Where(a => a.ShouldShowIcon(Character)); bool shouldDisplayAffliction(KeyValuePair<Affliction, LimbHealth> kvp, LimbHealth limbHealth)
thisAfflictions = thisAfflictions.Concat(afflictions.Where(a =>
{ {
Limb indicatorLimb = Character.AnimController.GetLimb(a.Prefab.IndicatorLimb); if (!kvp.Key.ShouldShowIcon(Character)) { return false; }
return indicatorLimb != null && indicatorLimb.HealthIndex == i && a.ShouldShowIcon(Character); if (kvp.Value == limbHealth)
})); {
return true;
}
else if (kvp.Value == null)
{
Limb indicatorLimb = Character.AnimController.GetLimb(kvp.Key.Prefab.IndicatorLimb);
return indicatorLimb != null && indicatorLimb.HealthIndex == i;
}
return false;
}
if (thisAfflictions.Count() <= 0) { i++; continue; } afflictionsDisplayedOnLimb.Clear();
foreach (var affliction in afflictions)
{
if (shouldDisplayAffliction(affliction, limbHealth)) { afflictionsDisplayedOnLimb.Add(affliction.Key); }
}
if (!afflictionsDisplayedOnLimb.Any()) { i++; continue; }
if (limbHealth.IndicatorSprite == null) { continue; } if (limbHealth.IndicatorSprite == null) { continue; }
float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height);
@@ -1776,12 +1811,12 @@ 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, excludeBuffs: false).FirstOrDefault(); Affliction mostSevereAffliction = SortAfflictionsBySeverity(afflictionsDisplayedOnLimb, 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 (afflictionsDisplayedOnLimb.Count() > 1)
{ {
string additionalAfflictionCount = $"+{thisAfflictions.Count() - 1}"; string additionalAfflictionCount = $"+{afflictionsDisplayedOnLimb.Count() - 1}";
Vector2 displace = GUI.SubHeadingFont.MeasureString(additionalAfflictionCount); Vector2 displace = GUI.SubHeadingFont.MeasureString(additionalAfflictionCount);
GUI.SubHeadingFont.DrawString(spriteBatch, additionalAfflictionCount, iconPos + new Vector2(displace.X * 1.1f, -displace.Y * 0.45f), Color.Black * 0.75f); GUI.SubHeadingFont.DrawString(spriteBatch, additionalAfflictionCount, iconPos + new Vector2(displace.X * 1.1f, -displace.Y * 0.45f), Color.Black * 0.75f);
GUI.SubHeadingFont.DrawString(spriteBatch, additionalAfflictionCount, iconPos + new Vector2(displace.X, -displace.Y * 0.5f), Color.White); GUI.SubHeadingFont.DrawString(spriteBatch, additionalAfflictionCount, iconPos + new Vector2(displace.X, -displace.Y * 0.5f), Color.White);
@@ -1834,8 +1869,7 @@ namespace Barotrauma
healthBarHolder.Visible = value; healthBarHolder.Visible = value;
} }
private readonly List<(AfflictionPrefab afflictionPrefab, float strength)> newAfflictions = new List<(AfflictionPrefab afflictionPrefab, float strength)>(); private readonly List<(LimbHealth limb, AfflictionPrefab afflictionPrefab, float strength)> newAfflictions = new List<(LimbHealth limb, AfflictionPrefab afflictionPrefab, float strength)>();
private readonly List<(LimbHealth limb, AfflictionPrefab afflictionPrefab, float strength)> newLimbAfflictions = new List<(LimbHealth limb, AfflictionPrefab afflictionPrefab, float strength)>();
private readonly List<(AfflictionPrefab.PeriodicEffect effect, float timer)> newPeriodicEffects = new List<(AfflictionPrefab.PeriodicEffect effect, float timer)>(); private readonly List<(AfflictionPrefab.PeriodicEffect effect, float timer)> newPeriodicEffects = new List<(AfflictionPrefab.PeriodicEffect effect, float timer)>();
public void ClientRead(IReadMessage inc) public void ClientRead(IReadMessage inc)
@@ -1865,47 +1899,9 @@ namespace Barotrauma
float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8); float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
newPeriodicEffects.Add((afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer)); newPeriodicEffects.Add((afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
} }
newAfflictions.Add((afflictionPrefab, afflictionStrength)); newAfflictions.Add((null, afflictionPrefab, afflictionStrength));
} }
foreach (Affliction affliction in afflictions)
{
//deactivate afflictions that weren't included in the network message
if (!newAfflictions.Any(a => a.afflictionPrefab == affliction.Prefab))
{
affliction.Strength = 0.0f;
}
}
foreach (var (afflictionPrefab, strength) in newAfflictions)
{
Affliction existingAffliction = afflictions.Find(a => a.Prefab == afflictionPrefab);
if (existingAffliction == null)
{
existingAffliction = afflictionPrefab.Instantiate(strength);
afflictions.Add(existingAffliction);
}
existingAffliction.SetStrength(strength);
if (existingAffliction == stunAffliction)
{
Character.SetStun(existingAffliction.Strength, true, true);
}
foreach (var periodicEffect in newPeriodicEffects)
{
if (!existingAffliction.Prefab.PeriodicEffects.Contains(periodicEffect.effect)) { continue; }
//timer has wrapped around, apply the effect
if (periodicEffect.timer - existingAffliction.PeriodicEffectTimers[periodicEffect.effect] > periodicEffect.effect.MinInterval / 2)
{
existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer;
foreach (StatusEffect effect in periodicEffect.effect.StatusEffects)
{
existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: null);
}
}
}
}
newLimbAfflictions.Clear();
byte limbAfflictionCount = inc.ReadByte(); byte limbAfflictionCount = inc.ReadByte();
for (int i = 0; i < limbAfflictionCount; i++) for (int i = 0; i < limbAfflictionCount; i++)
{ {
@@ -1931,43 +1927,50 @@ namespace Barotrauma
float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8); float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
newPeriodicEffects.Add((afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer)); newPeriodicEffects.Add((afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
} }
newLimbAfflictions.Add((limbHealths[limbIndex], afflictionPrefab, afflictionStrength)); newAfflictions.Add((limbHealths[limbIndex], afflictionPrefab, afflictionStrength));
} }
foreach (LimbHealth limbHealth in limbHealths) foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
{ {
foreach (Affliction affliction in limbHealth.Afflictions) //deactivate afflictions that weren't included in the network message
if (!newAfflictions.Any(a => kvp.Key.Prefab == a.afflictionPrefab && kvp.Value == a.limb))
{ {
//deactivate afflictions that weren't included in the network message kvp.Key.Strength = 0.0f;
if (!newLimbAfflictions.Any(a => a.limb == limbHealth && a.afflictionPrefab == affliction.Prefab)) }
}
foreach (var (limb, afflictionPrefab, strength) in newAfflictions)
{
Affliction existingAffliction = null;
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
{
if (kvp.Key.Prefab == afflictionPrefab && kvp.Value == limb)
{ {
affliction.Strength = 0.0f; existingAffliction = kvp.Key;
break;
} }
} }
if (existingAffliction == null)
foreach (var (limb, afflictionPrefab, strength) in newLimbAfflictions)
{ {
if (limb != limbHealth) { continue; } existingAffliction = afflictionPrefab.Instantiate(strength);
Affliction existingAffliction = limbHealth.Afflictions.Find(a => a.Prefab == afflictionPrefab); afflictions.Add(existingAffliction, limb);
if (existingAffliction == null) }
existingAffliction.SetStrength(strength);
if (existingAffliction == stunAffliction)
{
Character.SetStun(existingAffliction.Strength, true, true);
}
foreach (var periodicEffect in newPeriodicEffects)
{
if (!existingAffliction.Prefab.PeriodicEffects.Contains(periodicEffect.effect)) { continue; }
//timer has wrapped around, apply the effect
if (periodicEffect.timer - existingAffliction.PeriodicEffectTimers[periodicEffect.effect] > periodicEffect.effect.MinInterval / 2)
{ {
existingAffliction = afflictionPrefab.Instantiate(strength); existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer;
limbHealth.Afflictions.Add(existingAffliction); foreach (StatusEffect effect in periodicEffect.effect.StatusEffects)
}
existingAffliction.SetStrength(strength);
foreach (var periodicEffect in newPeriodicEffects)
{
if (!existingAffliction.Prefab.PeriodicEffects.Contains(periodicEffect.effect)) { continue; }
//timer has wrapped around, apply the effect
if (periodicEffect.timer - existingAffliction.PeriodicEffectTimers[periodicEffect.effect] > periodicEffect.effect.MinInterval / 2)
{ {
existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer; Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == limbHealths.IndexOf(limb));
foreach (StatusEffect effect in periodicEffect.effect.StatusEffects) existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: targetLimb);
{
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == limbHealths.IndexOf(limb));
existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: targetLimb);
}
} }
} }
} }
@@ -1985,14 +1988,13 @@ namespace Barotrauma
limb.BurnOverlayStrength = 0.0f; limb.BurnOverlayStrength = 0.0f;
limb.DamageOverlayStrength = 0.0f; limb.DamageOverlayStrength = 0.0f;
if (limbHealths[limb.HealthIndex].Afflictions.Count == 0) continue; foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
foreach (Affliction a in limbHealths[limb.HealthIndex].Afflictions)
{ {
limb.BurnOverlayStrength += a.Strength / Math.Min(a.Prefab.MaxStrength, 100) * a.Prefab.BurnOverlayAlpha; if (kvp.Value != limbHealths[limb.HealthIndex]) { continue; }
limb.DamageOverlayStrength += a.Strength / Math.Min(a.Prefab.MaxStrength, 100) * a.Prefab.DamageOverlayAlpha; var affliction = kvp.Key;
limb.BurnOverlayStrength += affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.BurnOverlayAlpha;
limb.DamageOverlayStrength += affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.DamageOverlayAlpha;
} }
limb.BurnOverlayStrength /= limbHealths[limb.HealthIndex].Afflictions.Count;
limb.DamageOverlayStrength /= limbHealths[limb.HealthIndex].Afflictions.Count;
} }
} }

View File

@@ -553,6 +553,12 @@ namespace Barotrauma
levelGenerationParams = LevelGenerationParams.LevelParams.FirstOrDefault(p => p.Identifier == levelGenerationIdentifier); levelGenerationParams = LevelGenerationParams.LevelParams.FirstOrDefault(p => p.Identifier == levelGenerationIdentifier);
} }
if (SubmarineInfo.SavedSubmarines.None(s => s.Name.ToLowerInvariant() == subName.ToLowerInvariant()))
{
ThrowError($"Cannot find a sub that matches the name \"{subName}\".");
return;
}
GameMain.MainMenuScreen.QuickStart(fixedSeed: false, subName, difficulty, levelGenerationParams); GameMain.MainMenuScreen.QuickStart(fixedSeed: false, subName, difficulty, levelGenerationParams);
}, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().ToArray() })); }, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().ToArray() }));
@@ -1461,10 +1467,11 @@ namespace Barotrauma
{ {
foreach (ItemPrefab ingredientItemPrefab in ingredient.ItemPrefabs) foreach (ItemPrefab ingredientItemPrefab in ingredient.ItemPrefabs)
{ {
NewMessage(" Its ingredient " + ingredientItemPrefab.Name + " has base cost " + ingredientItemPrefab.DefaultPrice.Price); int defaultPrice = ingredientItemPrefab.DefaultPrice?.Price ?? 0;
totalPrice += ingredientItemPrefab.DefaultPrice.Price; NewMessage(" Its ingredient " + ingredientItemPrefab.Name + " has base cost " + defaultPrice);
totalPrice += defaultPrice;
totalBestPrice += ingredientItemPrefab.GetMinPrice(); totalBestPrice += ingredientItemPrefab.GetMinPrice();
int basePrice = ingredientItemPrefab.DefaultPrice.Price; int basePrice = defaultPrice;
foreach (KeyValuePair<string, PriceInfo> ingredientItemLocationPrice in ingredientItemPrefab.GetBuyPricesUnder()) foreach (KeyValuePair<string, PriceInfo> ingredientItemLocationPrice in ingredientItemPrefab.GetBuyPricesUnder())
{ {
if (basePrice > ingredientItemLocationPrice.Value.Price) if (basePrice > ingredientItemLocationPrice.Value.Price)
@@ -1630,7 +1637,7 @@ namespace Barotrauma
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == parentItem); var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == parentItem);
int totalValue = 0; int totalValue = 0;
NewMessage(parentItem.Name + " has the price " + parentItem.DefaultPrice.Price); NewMessage(parentItem.Name + " has the price " + (parentItem.DefaultPrice?.Price ?? 0));
if (fabricationRecipe != null) if (fabricationRecipe != null)
{ {
NewMessage(" It constructs from:"); NewMessage(" It constructs from:");
@@ -1639,8 +1646,9 @@ namespace Barotrauma
{ {
foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs) foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs)
{ {
NewMessage(" " + itemPrefab.Name + " has the price " + itemPrefab.DefaultPrice.Price); int defaultPrice = itemPrefab.DefaultPrice?.Price ?? 0;
totalValue += itemPrefab.DefaultPrice.Price; NewMessage(" " + itemPrefab.Name + " has the price " + defaultPrice);
totalValue += defaultPrice;
} }
} }
NewMessage("Its total value was: " + totalValue); NewMessage("Its total value was: " + totalValue);
@@ -1651,10 +1659,16 @@ namespace Barotrauma
{ {
ItemPrefab itemPrefab = ItemPrefab itemPrefab =
(MapEntityPrefab.Find(deconstructItem.ItemIdentifier, identifier: null, showErrorMessages: false) ?? (MapEntityPrefab.Find(deconstructItem.ItemIdentifier, identifier: null, showErrorMessages: false) ??
MapEntityPrefab.Find(null, identifier: itemNameOrId, showErrorMessages: false)) as ItemPrefab; MapEntityPrefab.Find(null, identifier: deconstructItem.ItemIdentifier, showErrorMessages: false)) as ItemPrefab;
if (itemPrefab == null)
{
ThrowError($" Couldn't find deconstruct product \"{deconstructItem.ItemIdentifier}\"!");
continue;
}
NewMessage(" " + itemPrefab.Name + " has the price " + itemPrefab.DefaultPrice.Price); int defaultPrice = itemPrefab.DefaultPrice?.Price ?? 0;
totalValue += itemPrefab.DefaultPrice.Price; NewMessage(" " + itemPrefab.Name + " has the price " + defaultPrice);
totalValue += defaultPrice;
} }
NewMessage("Its deconstruct value was: " + totalValue); NewMessage("Its deconstruct value was: " + totalValue);
@@ -2485,8 +2499,6 @@ namespace Barotrauma
NewMessage("Resolution set to 0 x 0 (screen resolution will be used)", Color.Green); NewMessage("Resolution set to 0 x 0 (screen resolution will be used)", Color.Green);
NewMessage("Fullscreen enabled", Color.Green); NewMessage("Fullscreen enabled", Color.Green);
GameSettings.ShowUserStatisticsPrompt = true;
GameSettings.VerboseLogging = false; GameSettings.VerboseLogging = false;
if (GameMain.Config.MasterServerUrl != "http://www.undertowgames.com/baromaster") if (GameMain.Config.MasterServerUrl != "http://www.undertowgames.com/baromaster")
@@ -3162,7 +3174,7 @@ namespace Barotrauma
{ {
string errorMsg = "Failed to spawn a submarine. Arguments: \"" + string.Join(" ", args) + "\"."; string errorMsg = "Failed to spawn a submarine. Arguments: \"" + string.Join(" ", args) + "\".";
ThrowError(errorMsg, e); ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("DebugConsole.SpawnSubmarine:Error", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + '\n' + e.Message + '\n' + e.StackTrace.CleanupStackTrace()); GameAnalyticsManager.AddErrorEventOnce("DebugConsole.SpawnSubmarine:Error", GameAnalyticsManager.ErrorSeverity.Error, errorMsg + '\n' + e.Message + '\n' + e.StackTrace.CleanupStackTrace());
} }
}, },
() => () =>

View File

@@ -363,7 +363,7 @@ namespace Barotrauma
OnSecondaryClicked = (_, o) => OnSecondaryClicked = (_, o) =>
{ {
if (!(o is Client client)) { return false; } if (!(o is Client client)) { return false; }
GameMain.GameSession?.CrewManager?.CreateModerationContextMenu(PlayerInput.MousePosition.ToPoint(), client); NetLobbyScreen.CreateModerationContextMenu(client);
return true; return true;
}, },
Text = senderName Text = senderName
@@ -397,6 +397,7 @@ namespace Barotrauma
if (GameMain.NetLobbyScreen != null && GameMain.NetworkMember != null) if (GameMain.NetLobbyScreen != null && GameMain.NetworkMember != null)
{ {
clickableArea.OnClick = GameMain.NetLobbyScreen.SelectPlayer; clickableArea.OnClick = GameMain.NetLobbyScreen.SelectPlayer;
clickableArea.OnSecondaryClick = GameMain.NetLobbyScreen.ShowPlayerContextMenu;
} }
msgText.ClickableAreas.Add(clickableArea); msgText.ClickableAreas.Add(clickableArea);
} }

View File

@@ -79,6 +79,8 @@ namespace Barotrauma
public bool AllowMouseWheelScroll { get; set; } = true; public bool AllowMouseWheelScroll { get; set; } = true;
public bool AllowArrowKeyScroll { get; set; } = true;
/// <summary> /// <summary>
/// Scrolls the list smoothly /// Scrolls the list smoothly
/// </summary> /// </summary>
@@ -1254,10 +1256,16 @@ namespace Barotrauma
switch (key) switch (key)
{ {
case Keys.Down: case Keys.Down:
SelectNext(); if (!isHorizontal && AllowArrowKeyScroll) { SelectNext(); }
break; break;
case Keys.Up: case Keys.Up:
SelectPrevious(); if (!isHorizontal && AllowArrowKeyScroll) { SelectPrevious(); }
break;
case Keys.Left:
if (isHorizontal && AllowArrowKeyScroll) { SelectPrevious(); }
break;
case Keys.Right:
if (isHorizontal && AllowArrowKeyScroll) { SelectNext(); }
break; break;
case Keys.Enter: case Keys.Enter:
case Keys.Space: case Keys.Space:

View File

@@ -30,7 +30,7 @@ namespace Barotrauma
{ {
GameAnalyticsManager.AddErrorEventOnce( GameAnalyticsManager.AddErrorEventOnce(
"GUIProgressBar.BarSize_setter", "GUIProgressBar.BarSize_setter",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
"Attempted to set the BarSize of a GUIProgressBar to an invalid value (" + value + ")\n" + Environment.StackTrace.CleanupStackTrace()); "Attempted to set the BarSize of a GUIProgressBar to an invalid value (" + value + ")\n" + Environment.StackTrace.CleanupStackTrace());
return; return;
} }
@@ -105,7 +105,7 @@ namespace Barotrauma
{ {
GameAnalyticsManager.AddErrorEventOnce( GameAnalyticsManager.AddErrorEventOnce(
"GUIProgressBar.Draw:GetProgress", "GUIProgressBar.Draw:GetProgress",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
"ProgressGetter of a GUIProgressBar (" + ProgressGetter.Target.ToString() + " - " + ProgressGetter.Method.ToString() + ") returned an invalid value (" + newSize + ")\n" + Environment.StackTrace.CleanupStackTrace()); "ProgressGetter of a GUIProgressBar (" + ProgressGetter.Target.ToString() + " - " + ProgressGetter.Method.ToString() + ") returned an invalid value (" + newSize + ")\n" + Environment.StackTrace.CleanupStackTrace());
} }
else else

View File

@@ -276,6 +276,7 @@ namespace Barotrauma
public delegate void OnClickDelegate(GUITextBlock textBlock, ClickableArea area); public delegate void OnClickDelegate(GUITextBlock textBlock, ClickableArea area);
public OnClickDelegate OnClick; public OnClickDelegate OnClick;
public OnClickDelegate OnSecondaryClick;
} }
public List<ClickableArea> ClickableAreas { get; private set; } = new List<ClickableArea>(); public List<ClickableArea> ClickableAreas { get; private set; } = new List<ClickableArea>();
@@ -528,7 +529,7 @@ namespace Barotrauma
{ {
base.Update(deltaTime); base.Update(deltaTime);
if (ClickableAreas.Any() && (GUI.MouseOn?.IsParentOf(this) ?? true)) if (ClickableAreas.Any() && ((GUI.MouseOn?.IsParentOf(this) ?? true) || GUI.MouseOn == this))
{ {
if (!Rect.Contains(PlayerInput.MousePosition)) { return; } if (!Rect.Contains(PlayerInput.MousePosition)) { return; }
int index = GetCaretIndexFromScreenPos(PlayerInput.MousePosition); int index = GetCaretIndexFromScreenPos(PlayerInput.MousePosition);
@@ -541,6 +542,10 @@ namespace Barotrauma
{ {
clickableArea.OnClick?.Invoke(this, clickableArea); clickableArea.OnClick?.Invoke(this, clickableArea);
} }
if (PlayerInput.SecondaryMouseButtonClicked())
{
clickableArea.OnSecondaryClick?.Invoke(this, clickableArea);
}
break; break;
} }
} }

View File

@@ -562,7 +562,7 @@ namespace Barotrauma
frame.OnSecondaryClicked += (component, data) => frame.OnSecondaryClicked += (component, data) =>
{ {
GameMain.GameSession?.CrewManager?.CreateModerationContextMenu(PlayerInput.MousePosition.ToPoint(), client); NetLobbyScreen.CreateModerationContextMenu(client);
return true; return true;
}; };
@@ -917,7 +917,8 @@ namespace Barotrauma
textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea() textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea()
{ {
Data = data, Data = data,
OnClick = GameMain.NetLobbyScreen.SelectPlayer OnClick = GameMain.NetLobbyScreen.SelectPlayer,
OnSecondaryClick = GameMain.NetLobbyScreen.ShowPlayerContextMenu
}); });
} }
} }
@@ -1486,12 +1487,13 @@ namespace Barotrauma
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft), experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
barSize: controlledCharacter.Info.GetProgressTowardsNextLevel(), color: GUI.Style.Green) barSize: controlledCharacter.Info.GetProgressTowardsNextLevel(), color: GUI.Style.Green)
{ {
IsHorizontal = true IsHorizontal = true,
}; };
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUI.Font, textAlignment: Alignment.CenterRight) experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUI.Font, textAlignment: Alignment.CenterRight)
{ {
Shadow = true Shadow = true,
ToolTip = TextManager.Get("experiencetooltip")
}; };
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUI.SubHeadingFont, parseRichText: true, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true }; talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUI.SubHeadingFont, parseRichText: true, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true };
@@ -1643,6 +1645,7 @@ namespace Barotrauma
GameMain.Client.CreateEntityEvent(controlledCharacter, new object[] { NetEntityEvent.Type.UpdateTalents }); GameMain.Client.CreateEntityEvent(controlledCharacter, new object[] { NetEntityEvent.Type.UpdateTalents });
} }
} }
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
UpdateTalentButtons(); UpdateTalentButtons();
} }

View File

@@ -846,23 +846,27 @@ namespace Barotrauma
var currentOrPending = item.PendingItemSwap ?? item.Prefab; var currentOrPending = item.PendingItemSwap ?? item.Prefab;
string name = currentOrPending.Name; string name = currentOrPending.Name;
string quantityText = ""; string nameWithQuantity = "";
if (linkedItems.Count > 1) if (linkedItems.Count > 1)
{ {
foreach (ItemPrefab distinctItem in linkedItems.Select(it => it.Prefab).Distinct()) foreach (ItemPrefab distinctItem in linkedItems.Select(it => it.Prefab).Distinct())
{ {
if (quantityText != string.Empty) if (nameWithQuantity != string.Empty)
{ {
quantityText += ", "; nameWithQuantity += ", ";
} }
int count = linkedItems.Count(it => it.Prefab == distinctItem); int count = linkedItems.Count(it => it.Prefab == distinctItem);
quantityText += distinctItem.Name; nameWithQuantity += distinctItem.Name;
if (count > 1) if (count > 1)
{ {
quantityText += " " + TextManager.GetWithVariable("campaignstore.quantity", "[amount]", count.ToString()); nameWithQuantity += " " + TextManager.GetWithVariable("campaignstore.quantity", "[amount]", count.ToString());
} }
} }
} }
else
{
nameWithQuantity = name;
}
bool isOpen = false; bool isOpen = false;
GUIButton toggleButton = new GUIButton(rectT(1f, 0.1f, parent.Content), text: string.Empty, style: "SlideDown") GUIButton toggleButton = new GUIButton(rectT(1f, 0.1f, parent.Content), text: string.Empty, style: "SlideDown")
@@ -884,7 +888,7 @@ namespace Barotrauma
new GUITextBlock(rectT(0.3f, 1f, buttonLayout), text: slotText, font: GUI.SubHeadingFont); new GUITextBlock(rectT(0.3f, 1f, buttonLayout), text: slotText, font: GUI.SubHeadingFont);
GUILayoutGroup group = new GUILayoutGroup(rectT(0.7f, 1f, buttonLayout), isHorizontal: true) { Stretch = true }; GUILayoutGroup group = new GUILayoutGroup(rectT(0.7f, 1f, buttonLayout), isHorizontal: true) { Stretch = true };
string title = item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : quantityText; string title = item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : nameWithQuantity;
GUITextBlock text = new GUITextBlock(rectT(0.7f, 1f, group), text: title, font: GUI.SubHeadingFont, textAlignment: Alignment.Right, parseRichText: true) GUITextBlock text = new GUITextBlock(rectT(0.7f, 1f, group), text: title, font: GUI.SubHeadingFont, textAlignment: Alignment.Right, parseRichText: true)
{ {
TextColor = GUI.Style.Orange TextColor = GUI.Style.Orange
@@ -907,7 +911,7 @@ namespace Barotrauma
if (isUninstallPending) { canUninstall = false; } if (isUninstallPending) { canUninstall = false; }
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), currentOrPending.UpgradePreviewSprite, frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), currentOrPending.UpgradePreviewSprite,
item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : TextManager.GetWithVariable("upgrades.installeditem", "[itemname]", quantityText), item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : TextManager.GetWithVariable("upgrades.installeditem", "[itemname]", nameWithQuantity),
currentOrPending.Description, currentOrPending.Description,
0, null, addBuyButton: canUninstall, addProgressBar: false, buttonStyle: "WeaponUninstallButton")); 0, null, addBuyButton: canUninstall, addProgressBar: false, buttonStyle: "WeaponUninstallButton"));

View File

@@ -0,0 +1,114 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Barotrauma.IO;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
public static partial class GameAnalyticsManager
{
static partial void CreateConsentPrompt()
{
if (consentTextAvailable)
{
var background = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker");
var frame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.7f), background.RectTransform, Anchor.Center) { MaxSize = new Point(800, int.MaxValue) });
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), frame.RectTransform, Anchor.Center))
{
Stretch = true,
AbsoluteSpacing = GUI.IntScale(15)
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.Get("statisticsconsentheader"), font: GUI.SubHeadingFont, textColor: Color.White);
var mainText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.Get("statisticsconsenttext"), wrap: true, parseRichText: true);
foreach (var data in mainText.RichTextData)
{
mainText.ClickableAreas.Add(new GUITextBlock.ClickableArea()
{
Data = data,
OnClick = (GUITextBlock component, GUITextBlock.ClickableArea area) =>
{
GameMain.Instance.ShowOpenUrlInWebBrowserPrompt("https://gameanalytics.com/privacy/");
}
});
}
string privacyPolicyText = File.ReadAllText("daedalic_privacypolicy.txt");
var privacyPolicyBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform) { MaxSize = new Point(int.MaxValue, GUI.IntScale(200)) });
var privacyPolicy = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), privacyPolicyBox.Content.RectTransform), privacyPolicyText, wrap: true)
{
CanBeFocused = false
};
privacyPolicy.RectTransform.MinSize = new Point(0, (int)privacyPolicy.TextSize.Y);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.Get("statisticsconsentstatement"), wrap: true);
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), isHorizontal: true);
void buttonContainerSpacing(float width)
=> new GUIFrame(new RectTransform(new Vector2(width, 1.0f), buttonContainer.RectTransform), style: null);
buttonContainerSpacing(0.1f);
var yesBtn = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("Yes"));
yesBtn.OnClicked += (btn, userdata) =>
{
GUIMessageBox.MessageBoxes.Remove(background);
SetConsentInternal(Consent.Yes);
return true;
};
yesBtn.Enabled = false;
IEnumerable<CoroutineStatus> enableAfterTime(WaitForSeconds time, params GUIComponent[] components)
{
yield return time;
foreach (var c in components)
{
c.Enabled = true;
}
yield return CoroutineStatus.Success;
}
buttonContainerSpacing(0.2f);
var noBtn = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("No"));
noBtn.OnClicked += (btn, userdata) =>
{
GUIMessageBox.MessageBoxes.Remove(background);
SetConsent(Consent.No);
return true;
};
noBtn.Enabled = false;
CoroutineManager.StartCoroutine(enableAfterTime(new WaitForSeconds(0.3f), yesBtn, noBtn));
buttonContainerSpacing(0.1f);
buttonContainer.RectTransform.MinSize = new Point(0, yesBtn.RectTransform.MinSize.Y);
buttonContainer.RectTransform.MaxSize = new Point(int.MaxValue, yesBtn.RectTransform.MinSize.Y);
foreach (var child in content.Children)
{
if (child is GUITextBlock textBlock)
{
textBlock.RectTransform.MinSize = new Point(0, (int)textBlock.TextSize.Y);
textBlock.RectTransform.MaxSize = new Point(int.MaxValue, (int)textBlock.TextSize.Y + GUI.IntScale(15));
}
}
frame.RectTransform.MaxSize = new Point(
frame.RectTransform.MaxSize.X,
(int)(content.Children.Sum(c => c.RectTransform.MaxSize.Y + content.AbsoluteSpacing) / content.RectTransform.RelativeSize.Y));
GUIMessageBox.MessageBoxes.Add(background);
}
else
{
//user statistics disabled by default if the prompt cannot be shown in the user's language
SetConsent(Consent.Unknown);
}
}
}
}

View File

@@ -11,7 +11,6 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using GameAnalyticsSDK.Net;
using Barotrauma.IO; using Barotrauma.IO;
using System.Threading; using System.Threading;
using Barotrauma.Tutorials; using Barotrauma.Tutorials;
@@ -384,51 +383,6 @@ namespace Barotrauma
loadingCoroutine = CoroutineManager.StartCoroutine(Load(canLoadInSeparateThread), "Load", canLoadInSeparateThread); loadingCoroutine = CoroutineManager.StartCoroutine(Load(canLoadInSeparateThread), "Load", canLoadInSeparateThread);
} }
private void InitUserStats()
{
return;
if (GameSettings.ShowUserStatisticsPrompt)
{
if (TextManager.ContainsTag("statisticspromptheader") && TextManager.ContainsTag("statisticsprompttext"))
{
var userStatsPrompt = new GUIMessageBox(
TextManager.Get("statisticspromptheader"),
TextManager.Get("statisticsprompttext"),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
userStatsPrompt.Buttons[0].OnClicked += (btn, userdata) =>
{
GameSettings.ShowUserStatisticsPrompt = false;
GameSettings.SendUserStatistics = true;
GameAnalyticsManager.Init();
Config.SaveNewPlayerConfig();
return true;
};
userStatsPrompt.Buttons[0].OnClicked += userStatsPrompt.Close;
userStatsPrompt.Buttons[1].OnClicked += (btn, userdata) =>
{
GameSettings.ShowUserStatisticsPrompt = false;
GameSettings.SendUserStatistics = false;
Config.SaveNewPlayerConfig();
return true;
};
userStatsPrompt.Buttons[1].OnClicked += userStatsPrompt.Close;
}
else
{
//user statistics enabled by default if the prompt cannot be shown in the user's language
GameSettings.ShowUserStatisticsPrompt = false;
GameSettings.SendUserStatistics = true;
GameAnalyticsManager.Init();
Config.SaveNewPlayerConfig();
}
}
else if (GameSettings.SendUserStatistics)
{
GameAnalyticsManager.Init();
}
}
public class LoadingException : Exception public class LoadingException : Exception
{ {
public LoadingException(Exception e) : base("Loading was interrupted due to an error.", innerException: e) public LoadingException(Exception e) : base("Loading was interrupted due to an error.", innerException: e)
@@ -522,14 +476,9 @@ namespace Barotrauma
DebugConsole.Log("Selected content packages: " + string.Join(", ", Config.AllEnabledPackages.Select(cp => cp.Name))); DebugConsole.Log("Selected content packages: " + string.Join(", ", Config.AllEnabledPackages.Select(cp => cp.Name)));
} }
#if DEBUG GameAnalyticsManager.InitIfConsented();
GameSettings.ShowUserStatisticsPrompt = false;
GameSettings.SendUserStatistics = false;
#endif
InitUserStats(); yield return CoroutineStatus.Running;
yield return CoroutineStatus.Running;
Debug.WriteLine("sounds"); Debug.WriteLine("sounds");
@@ -841,6 +790,8 @@ namespace Barotrauma
} }
#endif #endif
NetworkMember?.Update((float)Timing.Step);
if (!hasLoaded && !CoroutineManager.IsCoroutineRunning(loadingCoroutine)) if (!hasLoaded && !CoroutineManager.IsCoroutineRunning(loadingCoroutine))
{ {
throw new LoadingException(loadingCoroutine.Exception); throw new LoadingException(loadingCoroutine.Exception);
@@ -915,6 +866,11 @@ namespace Barotrauma
{ {
gameSession.ToggleTabMenu(); gameSession.ToggleTabMenu();
} }
else if (GUIMessageBox.VisibleBox as GUIMessageBox != null &&
GUIMessageBox.VisibleBox.UserData as string == "bugreporter")
{
((GUIMessageBox)GUIMessageBox.VisibleBox).Close();
}
else if (GUI.PauseMenuOpen) else if (GUI.PauseMenuOpen)
{ {
GUI.TogglePauseMenu(); GUI.TogglePauseMenu();
@@ -999,11 +955,11 @@ namespace Barotrauma
} }
} }
NetworkMember?.Update((float)Timing.Step);
GUI.Update((float)Timing.Step); GUI.Update((float)Timing.Step);
} }
NetworkMember?.Update((float)Timing.Step);
CoroutineManager.Update((float)Timing.Step, Paused ? 0.0f : (float)Timing.Step); CoroutineManager.Update((float)Timing.Step, Paused ? 0.0f : (float)Timing.Step);
SteamManager.Update((float)Timing.Step); SteamManager.Update((float)Timing.Step);
@@ -1123,6 +1079,10 @@ namespace Barotrauma
if (GameSession != null) if (GameSession != null)
{ {
double roundDuration = Timing.TotalTime - GameSession.RoundStartTime;
GameAnalyticsManager.AddProgressionEvent(GameAnalyticsManager.ProgressionStatus.Fail,
GameSession.GameMode?.Name ?? "none",
roundDuration);
if (Tutorial.Initialized) if (Tutorial.Initialized)
{ {
((TutorialMode)GameSession.GameMode).Tutorial?.Stop(); ((TutorialMode)GameSession.GameMode).Tutorial?.Stop();
@@ -1131,6 +1091,7 @@ namespace Barotrauma
GUIMessageBox.CloseAll(); GUIMessageBox.CloseAll();
MainMenuScreen.Select(); MainMenuScreen.Select();
GameSession = null; GameSession = null;
} }
public void ShowCampaignDisclaimer(Action onContinue = null) public void ShowCampaignDisclaimer(Action onContinue = null)
@@ -1254,7 +1215,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Error while cleaning unnecessary save files", e); DebugConsole.ThrowError("Error while cleaning unnecessary save files", e);
} }
if (GameSettings.SendUserStatistics) { GameAnalytics.OnQuit(); } if (GameAnalyticsManager.SendUserStatistics) { GameAnalyticsManager.ShutDown(); }
if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); } if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); }
base.OnExiting(sender, args); base.OnExiting(sender, args);

View File

@@ -330,7 +330,7 @@ namespace Barotrauma
if (data == null) { return false; } if (data == null) { return false; }
if (GameMain.NetworkMember?.ConnectedClients?.Find(c => c.Character == data) is Client client) if (GameMain.NetworkMember?.ConnectedClients?.Find(c => c.Character == data) is Client client)
{ {
CreateModerationContextMenu(PlayerInput.MousePosition.ToPoint(), client); NetLobbyScreen.CreateModerationContextMenu(client);
return true; return true;
} }
return false; return false;
@@ -789,7 +789,7 @@ namespace Barotrauma
} }
else if (orderGiver != null) else if (orderGiver != null)
{ {
OrderChatMessage msg = new OrderChatMessage(order, option, priority, order?.TargetSpatialEntity ?? order?.TargetItemComponent?.Item as ISpatialEntity, character, orderGiver); OrderChatMessage msg = new OrderChatMessage(order, option, priority, order?.TargetSpatialEntity ?? order?.TargetItemComponent?.Item, character, orderGiver);
GameMain.Client?.SendChatMessage(msg); GameMain.Client?.SendChatMessage(msg);
} }
} }
@@ -968,7 +968,8 @@ namespace Barotrauma
{ {
if (!CanIssueOrders) { return false; } if (!CanIssueOrders) { return false; }
var orderInfo = (OrderInfo)userData; var orderInfo = (OrderInfo)userData;
SetCharacterOrder(character, orderInfo.Order, orderInfo.OrderOption, CharacterInfo.HighestManualOrderPriority, Character.Controlled); int priority = GetManualOrderPriority(character, orderInfo.Order);
SetCharacterOrder(character, orderInfo.Order, orderInfo.OrderOption, priority, Character.Controlled);
return true; return true;
}, },
OnSecondaryClicked = (button, userData) => OnSecondaryClicked = (button, userData) =>
@@ -1153,105 +1154,6 @@ namespace Barotrauma
} }
} }
#region Context Menu
public void CreateModerationContextMenu(Point mousePos, Client client)
{
if (GUIContextMenu.CurrentContextMenu != null) { return; }
if (IsSinglePlayer || client == null || ((!GameMain.Client?.PreviouslyConnectedClients?.Contains(client)) ?? true)) { return; }
bool hasSteam = client.SteamID > 0 && SteamManager.IsInitialized,
canKick = GameMain.Client.HasPermission(ClientPermissions.Kick),
canBan = GameMain.Client.HasPermission(ClientPermissions.Ban) && client.AllowKicking,
canPromo = GameMain.Client.HasPermission(ClientPermissions.ManagePermissions);
// Disable options if we are targeting ourselves
if (client.ID == GameMain.Client?.ID)
{
canKick = canBan = canPromo = false;
}
List<ContextMenuOption> options = new List<ContextMenuOption>();
options.Add(new ContextMenuOption("ViewSteamProfile", isEnabled: hasSteam, onSelected: delegate
{
Steamworks.SteamFriends.OpenWebOverlay($"https://steamcommunity.com/profiles/{client.SteamID}");
}));
options.Add(new ContextMenuOption("ModerationMenu.UserDetails", isEnabled: true, onSelected: delegate
{
GameMain.NetLobbyScreen?.SelectPlayer(client);
}));
// Creates sub context menu options for all the ranks
List<ContextMenuOption> permissionOptions = new List<ContextMenuOption>();
foreach (PermissionPreset rank in PermissionPreset.List)
{
permissionOptions.Add(new ContextMenuOption(rank.Name, isEnabled: true, onSelected: () =>
{
string label = TextManager.GetWithVariables(rank.Permissions == ClientPermissions.None ? "clearrankprompt" : "giverankprompt", new []{ "[user]", "[rank]" }, new []{ client.Name, rank.Name });
GUIMessageBox msgBox = new GUIMessageBox(string.Empty, label, new[] { TextManager.Get("Yes"), TextManager.Get("Cancel") });
msgBox.Buttons[0].OnClicked = delegate
{
client.SetPermissions(rank.Permissions, rank.PermittedCommands);
GameMain.Client.UpdateClientPermissions(client);
msgBox.Close();
return true;
};
msgBox.Buttons[1].OnClicked = delegate
{
msgBox.Close();
return true;
};
}) { Tooltip = rank.Description });
}
options.Add(new ContextMenuOption("Permissions", isEnabled: canPromo, options: permissionOptions.ToArray()));
Color clientColor = client.Character?.Info?.Job.Prefab.UIColor ?? Color.White;
if (GameMain.Client.ConnectedClients.Contains(client))
{
options.Add(new ContextMenuOption(client.MutedLocally ? "Unmute" : "Mute", isEnabled: client.ID != GameMain.Client?.ID, onSelected: delegate
{
client.MutedLocally = !client.MutedLocally;
}));
bool kickEnabled = client.ID != GameMain.Client?.ID && client.AllowKicking;
// if the user can kick create a kick option else create the votekick option
ContextMenuOption kickOption;
if (canKick)
{
kickOption = new ContextMenuOption("Kick", isEnabled: kickEnabled, onSelected: delegate
{
GameMain.Client?.CreateKickReasonPrompt(client.Name, false);
});
}
else
{
kickOption = new ContextMenuOption("VoteToKick", isEnabled: kickEnabled, onSelected: delegate
{
GameMain.Client?.VoteForKick(client);
});
}
options.Add(kickOption);
}
options.Add(new ContextMenuOption("Ban", isEnabled: canBan, onSelected: delegate
{
GameMain.Client?.CreateKickReasonPrompt(client.Name, true);
}));
GUIContextMenu.CreateContextMenu(null, client.Name, headerColor: clientColor, options.ToArray());
}
#endregion
public void AddToGUIUpdateList() public void AddToGUIUpdateList()
{ {
if (GUI.DisableHUD) { return; } if (GUI.DisableHUD) { return; }
@@ -1902,7 +1804,7 @@ namespace Barotrauma
private Hull hullContext; private Hull hullContext;
private WallSection wallContext; private WallSection wallContext;
private bool isContextual; private bool isContextual;
private readonly List<Order> contextualOrders = new List<Order>(); private readonly List<OrderInfo> contextualOrders = new List<OrderInfo>();
private Point shorcutCenterNodeOffset; private Point shorcutCenterNodeOffset;
private const int maxShortcutNodeCount = 4; private const int maxShortcutNodeCount = 4;
@@ -2605,7 +2507,7 @@ namespace Barotrauma
{ {
order = orders[i]; order = orders[i];
disableNode = !CanCharacterBeHeard() || disableNode = !CanCharacterBeHeard() ||
(order.MustSetTarget && (order.ItemComponentType != null || order.TargetItems.Length > 0) && (order.MustSetTarget && (order.ItemComponentType != null || order.GetTargetItems().Any() || order.RequireItems.Any()) &&
order.GetMatchingItems(true, interactableFor: characterContext ?? Character.Controlled).None()); order.GetMatchingItems(true, interactableFor: characterContext ?? Character.Controlled).None());
optionNodes.Add(new Tuple<GUIComponent, Keys>( optionNodes.Add(new Tuple<GUIComponent, Keys>(
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), order, (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false), CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), order, (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false),
@@ -2621,7 +2523,6 @@ namespace Barotrauma
if (contextualOrders.None()) if (contextualOrders.None())
{ {
string orderIdentifier; string orderIdentifier;
// Check if targeting an item or a hull // Check if targeting an item or a hull
if (itemContext != null && itemContext.IsPlayerTeamInteractable) if (itemContext != null && itemContext.IsPlayerTeamInteractable)
{ {
@@ -2630,84 +2531,89 @@ namespace Barotrauma
{ {
targetComponent = null; targetComponent = null;
if (p.UseController && itemContext.Components.None(c => c is Controller)) { continue; } if (p.UseController && itemContext.Components.None(c => c is Controller)) { continue; }
if ((p.TargetItems.Length > 0 && (p.TargetItems.Contains(itemContext.Prefab.Identifier) || itemContext.HasTag(p.TargetItems))) || if (p.HasOptionSpecificTargetItems)
p.TryGetTargetItemComponent(itemContext, out targetComponent))
{ {
contextualOrders.Add(p.HasOptions ? p : new Order(p, itemContext, targetComponent, Character.Controlled)); foreach (string option in p.Options)
{
if (p.TargetItemsMatchItem(itemContext, option))
{
contextualOrders.Add(new OrderInfo(new Order(p, itemContext, targetComponent, Character.Controlled), option));
}
}
}
else if (p.TargetItemsMatchItem(itemContext) || p.TryGetTargetItemComponent(itemContext, out targetComponent))
{
contextualOrders.Add(new OrderInfo(p.HasOptions ? p : new Order(p, itemContext, targetComponent, Character.Controlled), null));
} }
} }
// If targeting a periscope connected to a turret, show the 'operateweapons' order // If targeting a periscope connected to a turret, show the 'operateweapons' order
orderIdentifier = "operateweapons"; 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(info => info.Order.Identifier.Equals(orderIdentifier)) && itemContext.Components.Any(c => c is Controller))
{ {
var turret = itemContext.GetConnectedComponents<Turret>().FirstOrDefault(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)) ?? var turret = itemContext.GetConnectedComponents<Turret>().FirstOrDefault(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item)) ??
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)); itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item));
if (turret != null) { contextualOrders.Add(new Order(operateWeaponsPrefab, turret.Item, turret, Character.Controlled)); } if (turret != null)
{
contextualOrders.Add(new OrderInfo(new Order(operateWeaponsPrefab, turret.Item, turret, Character.Controlled), null));
}
} }
// If targeting a repairable item with condition below the repair threshold, show the 'repairsystems' order // If targeting a repairable item with condition below the repair threshold, show the 'repairsystems' order
orderIdentifier = "repairsystems"; orderIdentifier = "repairsystems";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Repairables.Any(r => r.IsBelowRepairThreshold)) if (contextualOrders.None(info => info.Order.Identifier.Equals(orderIdentifier)) && itemContext.Repairables.Any(r => r.IsBelowRepairThreshold))
{ {
if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical")))) if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical"))))
{ {
contextualOrders.Add(new Order(Order.GetPrefab("repairelectrical"), itemContext, targetItem: null, Character.Controlled)); contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab("repairelectrical"), itemContext, targetItem: null, Character.Controlled), null));
} }
else if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("mechanical")))) else if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("mechanical"))))
{ {
contextualOrders.Add(new Order(Order.GetPrefab("repairmechanical"), itemContext, targetItem: null, Character.Controlled)); contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab("repairmechanical"), itemContext, targetItem: null, Character.Controlled), null));
} }
else else
{ {
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled)); contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled), null));
} }
} }
// Remove the 'pumpwater' order if the target pump is auto-controlled (as it will immediately overwrite the work done by the bot) // Remove the 'pumpwater' order if the target pump is auto-controlled (as it will immediately overwrite the work done by the bot)
orderIdentifier = "pumpwater"; orderIdentifier = "pumpwater";
if (contextualOrders.FirstOrDefault(o => o.Identifier.Equals(orderIdentifier)) is Order o && if (contextualOrders.FirstOrDefault(info => info.Order.Identifier.Equals(orderIdentifier)) is OrderInfo pumpOrderInfo && pumpOrderInfo.Order is Order pumpOrder &&
itemContext.Components.FirstOrDefault(c => c.GetType() == o.ItemComponentType) is Pump pump) itemContext.Components.FirstOrDefault(c => c.GetType() == pumpOrder.ItemComponentType) is Pump pump && pump.IsAutoControlled)
{ {
if (pump.IsAutoControlled) { contextualOrders.Remove(o); } contextualOrders.Remove(pumpOrderInfo);
} }
if (contextualOrders.None()) if (contextualOrders.None())
{ {
orderIdentifier = "cleanupitems"; orderIdentifier = "cleanupitems";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier))) if (contextualOrders.None(info => info.Order.Identifier.Equals(orderIdentifier)))
{ {
if (AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled, checkInventory: false) || AIObjectiveCleanupItems.IsValidContainer(itemContext, Character.Controlled)) if (AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled, checkInventory: false) || AIObjectiveCleanupItems.IsValidContainer(itemContext, Character.Controlled))
{ {
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled)); contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled), null));
} }
} }
} }
AddIgnoreOrder(itemContext); AddIgnoreOrder(itemContext);
} }
else if (hullContext != null) else if (hullContext != null)
{ {
contextualOrders.Add(new Order(Order.GetPrefab("fixleaks"), hullContext, targetItem: null, Character.Controlled)); contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab("fixleaks"), hullContext, targetItem: null, Character.Controlled), null));
if (wallContext != null) if (wallContext != null)
{ {
AddIgnoreOrder(wallContext); AddIgnoreOrder(wallContext);
} }
} }
void AddIgnoreOrder(IIgnorable target) void AddIgnoreOrder(IIgnorable target)
{ {
var orderIdentifier = "ignorethis"; var orderIdentifier = "ignorethis";
if (!target.OrderedToBeIgnored && contextualOrders.None(o => o.Identifier == orderIdentifier)) if (!target.OrderedToBeIgnored && contextualOrders.None(info => info.Order.Identifier == orderIdentifier))
{ {
AddOrder(); AddOrder();
} }
else else
{ {
orderIdentifier = "unignorethis"; orderIdentifier = "unignorethis";
if (target.OrderedToBeIgnored && contextualOrders.None(o => o.Identifier == orderIdentifier)) if (target.OrderedToBeIgnored && contextualOrders.None(info => info.Order.Identifier == orderIdentifier))
{ {
AddOrder(); AddOrder();
} }
@@ -2717,64 +2623,61 @@ namespace Barotrauma
{ {
if (target is WallSection ws) if (target is WallSection ws)
{ {
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), ws.Wall, ws.Wall.Sections.IndexOf(ws), orderGiver: Character.Controlled)); contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab(orderIdentifier), ws.Wall, ws.Wall.Sections.IndexOf(ws), orderGiver: Character.Controlled), null));
} }
else else
{ {
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), target as Entity, null, Character.Controlled)); contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab(orderIdentifier), target as Entity, null, Character.Controlled), null));
} }
} }
} }
orderIdentifier = "wait"; orderIdentifier = "wait";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier))) if (contextualOrders.None(info => info.Order.Identifier.Equals(orderIdentifier)))
{ {
Vector2 position = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); Vector2 position = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
Hull hull = Hull.FindHull(position, guess: Character.Controlled?.CurrentHull); Hull hull = Hull.FindHull(position, guess: Character.Controlled?.CurrentHull);
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), new OrderTarget(position, hull), Character.Controlled)); contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab(orderIdentifier), new OrderTarget(position, hull), Character.Controlled), null));
} }
if (contextualOrders.None(info => info.Order.Category != OrderCategory.Movement) && characters.Any(c => c != Character.Controlled))
if (contextualOrders.None(o => o.Category != OrderCategory.Movement) && characters.Any(c => c != Character.Controlled))
{ {
orderIdentifier = "follow"; orderIdentifier = "follow";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier))) if (contextualOrders.None(info => info.Order.Identifier.Equals(orderIdentifier)))
{ {
contextualOrders.Add(Order.GetPrefab(orderIdentifier)); contextualOrders.Add(new OrderInfo(Order.GetPrefab(orderIdentifier), null));
} }
} }
// Show 'dismiss' order only when there are crew members with active orders // Show 'dismiss' order only when there are crew members with active orders
orderIdentifier = "dismissed"; orderIdentifier = "dismissed";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && characters.Any(c => !c.IsDismissed)) if (contextualOrders.None(info => info.Order.Identifier.Equals(orderIdentifier)) && characters.Any(c => !c.IsDismissed))
{ {
contextualOrders.Add(Order.GetPrefab(orderIdentifier)); contextualOrders.Add(new OrderInfo(Order.GetPrefab(orderIdentifier), null));
} }
} }
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 = !CanCharacterBeHeard(); bool disableNode = !CanCharacterBeHeard();
for (int i = 0; i < contextualOrders.Count; i++) for (int i = 0; i < contextualOrders.Count; i++)
{ {
optionNodes.Add(new Tuple<GUIComponent, Keys>( var info = contextualOrders[i];
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), contextualOrders[i], (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false), int hotkey = (i + 1) % 10;
!disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None)); var component = string.IsNullOrEmpty(info.OrderOption) ?
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), info.Order, hotkey, disableNode: disableNode, checkIfOrderCanBeHeard: false) :
CreateOrderOptionNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), info.Order, info.OrderOption, info.Order.Prefab.GetOptionName(info.OrderOption), hotkey);
optionNodes.Add(new Tuple<GUIComponent, Keys>(component, !disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None));
} }
} }
// TODO: there's duplicate logic here and above -> would be better to refactor so that the conditions are only defined in one place // TODO: there's duplicate logic here and above -> would be better to refactor so that the conditions are only defined in one place
public static bool DoesItemHaveContextualOrders(Item item) public static bool DoesItemHaveContextualOrders(Item item)
{ {
if (Order.PrefabList.Any(o => o.TargetItems.Length > 0 && o.TargetItems.Contains(item.Prefab.Identifier))) { return true; } if (Order.PrefabList.Any(o => o.TargetItemsMatchItem(item))) { return true; }
if (Order.PrefabList.Any(o => item.HasTag(o.TargetItems))) { return true; }
if (Order.PrefabList.Any(o => o.TryGetTargetItemComponent(item, out _))) { return true; } if (Order.PrefabList.Any(o => o.TryGetTargetItemComponent(item, out _))) { return true; }
if (AIObjectiveCleanupItems.IsValidTarget(item, Character.Controlled, checkInventory: false)) { return true; } if (AIObjectiveCleanupItems.IsValidTarget(item, Character.Controlled, checkInventory: false)) { return true; }
if (AIObjectiveCleanupItems.IsValidContainer(item, Character.Controlled)) { return true; } if (AIObjectiveCleanupItems.IsValidContainer(item, Character.Controlled)) { return true; }
if (Order.GetPrefab("loaditems") is Order loadItemsPrefab && AIObjectiveLoadItems.IsValidTarget(item, Character.Controlled, targetContainerTags: loadItemsPrefab.GetTargetItems())) { return true; }
if (item.Repairables.Any(r => r.IsBelowRepairThreshold)) { return true; } if (item.Repairables.Any(r => r.IsBelowRepairThreshold)) { return true; }
var operateWeaponsPrefab = Order.GetPrefab("operateweapons"); return Order.GetPrefab("operateweapons") is Order operateWeaponsPrefab && item.Components.Any(c => c is Controller) &&
return item.Components.Any(c => c is Controller) && (item.GetConnectedComponents<Turret>().Any(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item)) ||
(item.GetConnectedComponents<Turret>().Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)) || item.GetConnectedComponents<Turret>(recursive: true).Any(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item)));
item.GetConnectedComponents<Turret>(recursive: true).Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)));
} }
/// <param name="hotkey">Use a negative value (e.g. -1) if there should be no hotkey associated with the node</param> /// <param name="hotkey">Use a negative value (e.g. -1) if there should be no hotkey associated with the node</param>
@@ -2831,7 +2734,8 @@ namespace Barotrauma
o = new Order(o.Prefab, orderTargetEntity, orderTargetEntity.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: order.OrderGiver); o = new Order(o.Prefab, orderTargetEntity, orderTargetEntity.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: order.OrderGiver);
} }
var character = !o.TargetAllCharacters ? characterContext ?? GetCharacterForQuickAssignment(o) : null; var character = !o.TargetAllCharacters ? characterContext ?? GetCharacterForQuickAssignment(o) : null;
SetCharacterOrder(character, o, null, CharacterInfo.HighestManualOrderPriority, Character.Controlled); int priority = GetManualOrderPriority(character, o);
SetCharacterOrder(character, o, null, priority, Character.Controlled);
DisableCommandUI(); DisableCommandUI();
} }
return true; return true;
@@ -2943,7 +2847,9 @@ namespace Barotrauma
} }
else else
{ {
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, CharacterInfo.HighestManualOrderPriority, Character.Controlled); var character = characterContext ?? GetCharacterForQuickAssignment(o.Item1);
int priority = GetManualOrderPriority(character, o.Item1);
SetCharacterOrder(character, o.Item1, o.Item2, priority, Character.Controlled);
DisableCommandUI(); DisableCommandUI();
} }
return true; return true;
@@ -3013,7 +2919,9 @@ namespace Barotrauma
} }
else else
{ {
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, CharacterInfo.HighestManualOrderPriority, Character.Controlled); var character = characterContext ?? GetCharacterForQuickAssignment(o.Item1);
int priority = GetManualOrderPriority(character, o.Item1);
SetCharacterOrder(character, o.Item1, o.Item2, priority, Character.Controlled);
DisableCommandUI(); DisableCommandUI();
} }
return true; return true;
@@ -3218,7 +3126,9 @@ namespace Barotrauma
OnClicked = (_, userData) => OnClicked = (_, userData) =>
{ {
if (!CanIssueOrders) { return false; } if (!CanIssueOrders) { return false; }
SetCharacterOrder(userData as Character, order.Item1, order.Item2, CharacterInfo.HighestManualOrderPriority, Character.Controlled); var character = userData as Character;
int priority = GetManualOrderPriority(character, order.Item1);
SetCharacterOrder(character, order.Item1, order.Item2, priority, Character.Controlled);
DisableCommandUI(); DisableCommandUI();
return true; return true;
} }
@@ -3496,6 +3406,11 @@ namespace Barotrauma
return order.Name; return order.Name;
} }
private int GetManualOrderPriority(Character character, Order order)
{
return character?.Info?.GetManualOrderPriority(order) ?? CharacterInfo.HighestManualOrderPriority;
}
#region Crew Member Assignment Logic #region Crew Member Assignment Logic
private bool CanOpenManualAssignment(GUIComponent node) private bool CanOpenManualAssignment(GUIComponent node)
{ {

View File

@@ -65,7 +65,7 @@ namespace Barotrauma
public override void ShowStartMessage() public override void ShowStartMessage()
{ {
foreach (Mission mission in Missions) foreach (Mission mission in Missions.ToList())
{ {
new GUIMessageBox( new GUIMessageBox(
mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name, mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name,

View File

@@ -60,7 +60,7 @@ namespace Barotrauma
var newCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null); var newCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null);
var loadCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null); var loadCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null);
GameMain.NetLobbyScreen.CampaignSetupUI = new MultiPlayerCampaignSetupUI(newCampaignContainer, loadCampaignContainer, null, saveFiles); GameMain.NetLobbyScreen.CampaignSetupUI = new MultiPlayerCampaignSetupUI(newCampaignContainer, loadCampaignContainer, saveFiles);
var newCampaignButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainer.RectTransform), var newCampaignButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainer.RectTransform),
TextManager.Get("NewCampaign"), style: "GUITabButton") TextManager.Get("NewCampaign"), style: "GUITabButton")

View File

@@ -475,12 +475,12 @@ namespace Barotrauma
ItemComponent targetItem = null; ItemComponent targetItem = null;
if (orderPrefab.MustSetTarget) if (orderPrefab.MustSetTarget)
{ {
targetEntity = orderPrefab.GetMatchingItems(true, interactableFor: Character.Controlled).FirstOrDefault(); targetEntity = orderPrefab.GetMatchingItems(true, interactableFor: Character.Controlled, orderOption: orderInfo.option).FirstOrDefault();
if (targetEntity == null) { return; } if (targetEntity == null) { return; }
targetItem = orderPrefab.GetTargetItemComponent(targetEntity); targetItem = orderPrefab.GetTargetItemComponent(targetEntity);
} }
var order = new Order(orderPrefab, targetEntity as Entity, targetItem, orderGiver: Character.Controlled); var order = new Order(orderPrefab, targetEntity as Entity, targetItem, orderGiver: Character.Controlled);
GameMain.GameSession.CrewManager.SetCharacterOrder(Character.Controlled, order, orderInfo.option, CharacterInfo.HighestManualOrderPriority, Character.Controlled); GameMain.GameSession?.CrewManager?.SetCharacterOrder(Character.Controlled, order, orderInfo.option, CharacterInfo.HighestManualOrderPriority, Character.Controlled);
}); });
} }

View File

@@ -635,9 +635,9 @@ namespace Barotrauma
} }
else if (characterInfo.CauseOfDeath.Type == CauseOfDeathType.Affliction && characterInfo.CauseOfDeath.Affliction == null) else if (characterInfo.CauseOfDeath.Type == CauseOfDeathType.Affliction && characterInfo.CauseOfDeath.Affliction == null)
{ {
string errorMsg = "Character \"" + characterInfo.Name + "\" had an invalid cause of death (the type of the cause of death was Affliction, but affliction was not specified)."; string errorMsg = "Character \"[name]\" had an invalid cause of death (the type of the cause of death was Affliction, but affliction was not specified).";
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg.Replace("[name]", characterInfo.Name));
GameAnalyticsManager.AddErrorEventOnce("RoundSummary:InvalidCauseOfDeath", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("RoundSummary:InvalidCauseOfDeath", GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", characterInfo.SpeciesName));
statusText = TextManager.Get("CauseOfDeathDescription.Unknown"); statusText = TextManager.Get("CauseOfDeathDescription.Unknown");
statusColor = GUI.Style.Red; statusColor = GUI.Style.Red;
} }

View File

@@ -1,12 +1,10 @@
using Barotrauma.Extensions; using Barotrauma.Networking;
using Barotrauma.Networking;
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 OpenAL; using OpenAL;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -365,6 +363,12 @@ namespace Barotrauma
TextManager.Get("Settings"), textAlignment: Alignment.TopLeft, font: GUI.LargeFont) TextManager.Get("Settings"), textAlignment: Alignment.TopLeft, font: GUI.LargeFont)
{ ForceUpperCase = true }; { ForceUpperCase = true };
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), settingsTitle.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest), style: "GUIBugButton")
{
ToolTip = TextManager.Get("bugreportbutton") + $" (v{GameMain.Version})",
OnClicked = (btn, userdata) => { GameMain.Instance.ShowBugReporter(); return true; }
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform), TextManager.Get("ContentPackages"), font: GUI.SubHeadingFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform), TextManager.Get("ContentPackages"), font: GUI.SubHeadingFont);
var corePackageDropdown = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform)) var corePackageDropdown = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform))
@@ -509,17 +513,53 @@ namespace Barotrauma
ApplySettings(); ApplySettings();
GameMain.Instance.Exit(); GameMain.Instance.Exit();
return true; return true;
}; msgBox.Buttons[1].OnClicked += (btn, userdata) => };
msgBox.Buttons[1].OnClicked += (btn, userdata) =>
{ {
Language = prevLanguage; Language = prevLanguage;
languageDD.SelectItem(Language); languageDD.SelectItem(Language);
msgBox.Close(); msgBox.Close();
return true; return true;
}; };
return true; return true;
}; };
var statisticsTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.045f), leftPanel.RectTransform), TextManager.Get("statisticsconsenttickbox"))
{
OnSelected = (GUITickBox tickBox) =>
{
GameAnalyticsManager.SetConsent(
tickBox.Selected
? GameAnalyticsManager.Consent.Ask
: GameAnalyticsManager.Consent.No);
return false;
}
};
void updateGATickBoxToolTip()
=> statisticsTickBox.ToolTip = TextManager.Get($"GameAnalyticsStatus.{GameAnalyticsManager.UserConsented}");
updateGATickBoxToolTip();
var cachedConsent = GameAnalyticsManager.Consent.Unknown;
var statisticsTickBoxUpdater = new GUICustomComponent(
new RectTransform(Vector2.Zero, statisticsTickBox.RectTransform),
onUpdate: (deltaTime, component) =>
{
bool shouldTickBoxBeSelected = GameAnalyticsManager.UserConsented == GameAnalyticsManager.Consent.Yes;
bool shouldUpdateTickBoxState = cachedConsent != GameAnalyticsManager.UserConsented
|| statisticsTickBox.Selected != shouldTickBoxBeSelected;
if (!shouldUpdateTickBoxState) { return; }
updateGATickBoxToolTip();
cachedConsent = GameAnalyticsManager.UserConsented;
GUITickBox.OnSelectedHandler prevHandler = statisticsTickBox.OnSelected;
statisticsTickBox.OnSelected = null;
statisticsTickBox.Selected = shouldTickBoxBeSelected;
statisticsTickBox.OnSelected = prevHandler;
statisticsTickBox.Enabled = GameAnalyticsManager.UserConsented != GameAnalyticsManager.Consent.Error;
});
// right panel -------------------------------------- // right panel --------------------------------------
var rightPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.99f - leftPanel.RectTransform.RelativeSize.X, leftPanel.RectTransform.RelativeSize.Y), var rightPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.99f - leftPanel.RectTransform.RelativeSize.X, leftPanel.RectTransform.RelativeSize.Y),
@@ -556,13 +596,6 @@ namespace Barotrauma
tabButtons[(int)tab].Text = ToolBox.LimitString(buttonText, tabButtons[(int)tab].Font, (int)(0.75f * tabWidth * tabButtonHolder.Rect.Width)); tabButtons[(int)tab].Text = ToolBox.LimitString(buttonText, tabButtons[(int)tab].Font, (int)(0.75f * tabWidth * tabButtonHolder.Rect.Width));
} }
new GUIButton(new RectTransform(new Vector2(0.05f, 0.75f), tabButtonHolder.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.2f) }, style: "GUIBugButton")
{
ToolTip = TextManager.Get("bugreportbutton"),
OnClicked = (btn, userdata) => { GameMain.Instance.ShowBugReporter(); return true; }
};
/// Graphics tab -------------------------------------------------------------- /// Graphics tab --------------------------------------------------------------
var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Graphics].RectTransform, Anchor.TopLeft) var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Graphics].RectTransform, Anchor.TopLeft)
@@ -1168,7 +1201,7 @@ namespace Barotrauma
catch (Exception e) catch (Exception e)
{ {
DebugConsole.ThrowError("Failed to set voice capture mode.", e); DebugConsole.ThrowError("Failed to set voice capture mode.", e);
GameAnalyticsManager.AddErrorEventOnce("SetVoiceCaptureMode", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, "Failed to set voice capture mode. " + e.Message + "\n" + e.StackTrace.CleanupStackTrace()); GameAnalyticsManager.AddErrorEventOnce("SetVoiceCaptureMode", GameAnalyticsManager.ErrorSeverity.Error, "Failed to set voice capture mode. " + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
VoiceSetting = VoiceMode.Disabled; VoiceSetting = VoiceMode.Disabled;
} }

View File

@@ -406,7 +406,7 @@ namespace Barotrauma.Items.Components
DebugConsole.Log("Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume); DebugConsole.Log("Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
GameAnalyticsManager.AddErrorEventOnce( GameAnalyticsManager.AddErrorEventOnce(
"ItemComponent.PlaySound:" + item.Name + GetType().ToString(), "ItemComponent.PlaySound:" + item.Name + GetType().ToString(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
"Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume); "Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
return 0.0f; return 0.0f;
} }

View File

@@ -1,9 +1,6 @@
using System; using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -11,7 +8,7 @@ namespace Barotrauma.Items.Components
{ {
public float BackgroundSpriteDepth public float BackgroundSpriteDepth
{ {
get { return item.GetDrawDepth() + 0.1f; } get { return item.GetDrawDepth() + 0.05f; }
} }
public Vector2 DrawSize public Vector2 DrawSize

View File

@@ -672,36 +672,33 @@ namespace Barotrauma.Items.Components
return; return;
} }
if (currentMode == MiniMapMode.HullStatus) if (currentMode == MiniMapMode.HullStatus && item.Submarine != null)
{ {
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle; Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.End(); spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable); spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
spriteBatch.GraphicsDevice.ScissorRectangle = submarineContainer.Rect; spriteBatch.GraphicsDevice.ScissorRectangle = submarineContainer.Rect;
if (item.Submarine != null) var sprite = GUI.Style.UIGlowSolidCircular?.Sprite;
float alpha = (MathF.Sin(blipState / maxBlipState * MathHelper.TwoPi) + 1.5f) * 0.5f;
if (sprite != null)
{ {
var sprite = GUI.Style.UIGlowSolidCircular?.Sprite; Vector2 spriteSize = sprite.size;
float alpha = (MathF.Sin(blipState / maxBlipState * MathHelper.TwoPi) + 1.5f) * 0.5f; Rectangle worldBorders = item.Submarine.GetDockedBorders();
if (sprite != null) worldBorders.Location += item.Submarine.WorldPosition.ToPoint();
foreach (Gap gap in Gap.GapList)
{ {
Vector2 spriteSize = sprite.size; if (gap.IsRoomToRoom || gap.linkedTo.Count == 0 || gap.Submarine != item.Submarine || gap.ConnectedDoor != null || gap.HiddenInGame) { continue; }
Rectangle worldBorders = item.Submarine.GetDockedBorders(); RectangleF entityRect = ScaleRectToUI(gap, miniMapFrame.Rect, worldBorders);
worldBorders.Location += item.Submarine.WorldPosition.ToPoint();
foreach (Gap gap in Gap.GapList)
{
if (gap.IsRoomToRoom || gap.Submarine != item.Submarine || gap.ConnectedDoor != null || gap.HiddenInGame) { continue; }
RectangleF entityRect = ScaleRectToUI(gap, miniMapFrame.Rect, worldBorders);
Vector2 scale = new Vector2(entityRect.Size.X / spriteSize.X, entityRect.Size.Y / spriteSize.Y) * 2.0f; Vector2 scale = new Vector2(entityRect.Size.X / spriteSize.X, entityRect.Size.Y / spriteSize.Y) * 2.0f;
Color color = ToolBox.GradientLerp(gap.Open, GUI.Style.HealthBarColorMedium, GUI.Style.HealthBarColorLow) * alpha; Color color = ToolBox.GradientLerp(gap.Open, GUI.Style.HealthBarColorMedium, GUI.Style.HealthBarColorLow) * alpha;
sprite.Draw(spriteBatch, sprite.Draw(spriteBatch,
miniMapFrame.Rect.Location.ToVector2() + entityRect.Center, miniMapFrame.Rect.Location.ToVector2() + entityRect.Center,
color, origin: sprite.Origin, rotate: 0.0f, scale: scale); color, origin: sprite.Origin, rotate: 0.0f, scale: scale);
}
} }
} }
if (currentMode == MiniMapMode.HullStatus && hullStatusComponents != null) if (currentMode == MiniMapMode.HullStatus && hullStatusComponents != null)
{ {
@@ -991,8 +988,40 @@ namespace Barotrauma.Items.Components
continue; continue;
} }
hullData.HullOxygenAmount = RequireOxygenDetectors ? hullData.ReceivedOxygenAmount : hull.OxygenPercentage; if (RequireOxygenDetectors)
hullData.HullWaterAmount = RequireWaterDetectors ? hullData.ReceivedWaterAmount : Math.Min(hull.WaterVolume / hull.Volume, 1.0f); {
hullData.HullOxygenAmount = hullData.ReceivedOxygenAmount;
}
else if (hullData.LinkedHulls.Any())
{
hullData.HullOxygenAmount = 0.0f;
foreach (Hull linkedHull in hullData.LinkedHulls)
{
hullData.HullOxygenAmount += linkedHull.OxygenPercentage;
}
hullData.HullOxygenAmount /= hullData.LinkedHulls.Count;
}
else
{
hullData.HullOxygenAmount = hull.OxygenPercentage;
}
if (RequireWaterDetectors)
{
hullData.HullWaterAmount = hullData.ReceivedWaterAmount;
}
else if (hullData.LinkedHulls.Any())
{
hullData.HullWaterAmount = 0.0f;
foreach (Hull linkedHull in hullData.LinkedHulls)
{
hullData.HullWaterAmount += Math.Min(linkedHull.WaterVolume / linkedHull.Volume, 1.0f);
}
hullData.HullWaterAmount /= hullData.LinkedHulls.Count;
}
else
{
hullData.HullWaterAmount = Math.Min(hull.WaterVolume / hull.Volume, 1.0f);
}
float gapOpenSum = 0.0f; float gapOpenSum = 0.0f;
@@ -1013,15 +1042,6 @@ namespace Barotrauma.Items.Components
float? oxygenAmount = hullData.HullOxygenAmount, float? oxygenAmount = hullData.HullOxygenAmount,
waterAmount = hullData.HullWaterAmount; waterAmount = hullData.HullWaterAmount;
foreach (Hull linkedHull in hullData.LinkedHulls)
{
oxygenAmount += linkedHull.OxygenPercentage;
waterAmount += Math.Min(linkedHull.WaterVolume / linkedHull.Volume, 1.0f);
}
oxygenAmount /= (hullData.LinkedHulls.Count + 1);
waterAmount /= (hullData.LinkedHulls.Count + 1);
string line1 = gapOpenSum > 0.1f ? TextManager.Get("MiniMapHullBreach") : string.Empty; string line1 = gapOpenSum > 0.1f ? TextManager.Get("MiniMapHullBreach") : string.Empty;
Color line1Color = GUI.Style.Red; Color line1Color = GUI.Style.Red;
@@ -1111,10 +1131,9 @@ namespace Barotrauma.Items.Components
private void DrawHUDBack(SpriteBatch spriteBatch, GUICustomComponent container) private void DrawHUDBack(SpriteBatch spriteBatch, GUICustomComponent container)
{ {
if (item.Submarine != null) if (item.Submarine == null) { return; }
{
DrawSubmarine(spriteBatch); DrawSubmarine(spriteBatch);
}
if (Voltage < MinVoltage) { return; } if (Voltage < MinVoltage) { return; }
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle; Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
@@ -1155,7 +1174,8 @@ namespace Barotrauma.Items.Components
if (hullsVisible && hullData.HullWaterAmount is { } waterAmount) if (hullsVisible && hullData.HullWaterAmount is { } waterAmount)
{ {
if (hullFrame.Rect.Height * waterAmount > 3.0f) if (!RequireWaterDetectors) { waterAmount = hull.WaterPercentage / 100.0f; }
if (hullFrame.Rect.Height * waterAmount > 1.0f)
{ {
RectangleF waterRect = new RectangleF(hullFrame.Rect.X, hullFrame.Rect.Y + hullFrame.Rect.Height * (1.0f - waterAmount), hullFrame.Rect.Width, hullFrame.Rect.Height * waterAmount); RectangleF waterRect = new RectangleF(hullFrame.Rect.X, hullFrame.Rect.Y + hullFrame.Rect.Height * (1.0f - waterAmount), hullFrame.Rect.Width, hullFrame.Rect.Height * waterAmount);
@@ -1418,7 +1438,7 @@ namespace Barotrauma.Items.Components
{ {
if (linkedEntity is Hull linkedHull) if (linkedEntity is Hull linkedHull)
{ {
if (linkedHulls.Contains(linkedHull)) { continue; } if (linkedHulls.Contains(linkedHull) || linkedHull.HiddenInGame) { continue; }
linkedHulls.Add(linkedHull); linkedHulls.Add(linkedHull);
GetLinkedHulls(linkedHull, linkedHulls); GetLinkedHulls(linkedHull, linkedHulls);
} }
@@ -1598,7 +1618,7 @@ namespace Barotrauma.Items.Components
bool IsPartofSub(MapEntity entity) bool IsPartofSub(MapEntity entity)
{ {
if (entity.Submarine != sub && !connectedSubs.Contains(entity.Submarine)) { return false; } if (entity.Submarine != sub && !connectedSubs.Contains(entity.Submarine) || entity.HiddenInGame) { return false; }
return !settings.IgnoreOutposts || sub.IsEntityFoundOnThisSub(entity, true); return !settings.IgnoreOutposts || sub.IsEntityFoundOnThisSub(entity, true);
} }

View File

@@ -1623,6 +1623,11 @@ namespace Barotrauma.Items.Components
markerDistances.Add(targetIdentifier, cachedDistance); markerDistances.Add(targetIdentifier, cachedDistance);
dist = path.TotalLength; dist = path.TotalLength;
} }
else
{
var cachedDistance = new CachedDistance(transducerPosition, worldPosition, linearDist, Timing.TotalTime + Rand.Range(4.0f, 7.0f));
markerDistances.Add(targetIdentifier, cachedDistance);
}
} }
Vector2 position = worldPosition - transducerPosition; Vector2 position = worldPosition - transducerPosition;

View File

@@ -35,6 +35,13 @@ namespace Barotrauma.Items.Components
private FixActions requestStartFixAction; private FixActions requestStartFixAction;
private bool qteSuccess;
private float qteTimer;
private const float qteTime = 0.5f;
private float qteCooldown;
private const float qteCooldownTime = 0.5f;
public float FakeBrokenTimer; 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.")]
@@ -55,16 +62,13 @@ namespace Barotrauma.Items.Components
if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) { return false; } if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) { return false; }
if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; } if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; }
float maxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(character); float defaultMaxCondition = item.MaxCondition / item.MaxRepairConditionMultiplier;
float defaultMaxCondition = item.MaxCondition / maxRepairConditionMultiplier;
if (MathUtils.Percentage(item.Condition, defaultMaxCondition) < RepairThreshold) { return true; } if (MathUtils.Percentage(item.Condition, defaultMaxCondition) < RepairThreshold) { return true; }
if (CurrentFixer == character) if (CurrentFixer == character)
{ {
float condition = item.Condition / item.MaxRepairConditionMultiplier; if (item.Condition < item.MaxCondition)
float maxCondition = item.MaxCondition / item.MaxRepairConditionMultiplier;
if (condition < maxCondition * maxRepairConditionMultiplier)
{ {
return true; return true;
} }
@@ -145,11 +149,14 @@ namespace Barotrauma.Items.Components
progressBar = new GUIProgressBar(new RectTransform(new Vector2(0.6f, 1.0f), progressBarHolder.RectTransform), progressBar = new GUIProgressBar(new RectTransform(new Vector2(0.6f, 1.0f), progressBarHolder.RectTransform),
color: GUI.Style.Green, barSize: 0.0f, style: "DeviceProgressBar"); color: GUI.Style.Green, barSize: 0.0f, style: "DeviceProgressBar");
progressBarOverlayText = new GUITextBlock(new RectTransform(Vector2.One, progressBar.RectTransform), string.Empty, font: GUI.SubHeadingFont, textAlignment: Alignment.Center) progressBarOverlayText = new GUITextBlock(new RectTransform(Vector2.One, progressBar.RectTransform), string.Empty, font: GUI.SubHeadingFont, textAlignment: Alignment.Center)
{ {
IgnoreLayoutGroups = true IgnoreLayoutGroups = true
}; };
qteTimer = qteTime;
repairButtonText = TextManager.Get("RepairButton"); repairButtonText = TextManager.Get("RepairButton");
repairingText = TextManager.Get("Repairing"); repairingText = TextManager.Get("Repairing");
RepairButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), progressBarHolder.RectTransform, Anchor.TopCenter), repairButtonText) RepairButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), progressBarHolder.RectTransform, Anchor.TopCenter), repairButtonText)
@@ -159,6 +166,11 @@ namespace Barotrauma.Items.Components
requestStartFixAction = FixActions.Repair; requestStartFixAction = FixActions.Repair;
item.CreateClientEvent(this); item.CreateClientEvent(this);
return true; return true;
},
OnButtonDown = () =>
{
QTEAction();
return true;
} }
}; };
RepairButton.TextBlock.AutoScaleHorizontal = true; RepairButton.TextBlock.AutoScaleHorizontal = true;
@@ -183,6 +195,11 @@ namespace Barotrauma.Items.Components
requestStartFixAction = FixActions.Sabotage; requestStartFixAction = FixActions.Sabotage;
item.CreateClientEvent(this); item.CreateClientEvent(this);
return true; return true;
},
OnButtonDown = () =>
{
QTEAction();
return true;
} }
}; };
@@ -253,6 +270,21 @@ namespace Barotrauma.Items.Components
{ {
repairSoundChannel = SoundPlayer.PlaySound("repair", item.WorldPosition, hullGuess: item.CurrentHull); repairSoundChannel = SoundPlayer.PlaySound("repair", item.WorldPosition, hullGuess: item.CurrentHull);
} }
if (qteCooldown > 0.0f)
{
qteCooldown -= deltaTime;
if (qteCooldown <= 0.0f)
{
qteTimer = qteTime;
}
}
else
{
qteTimer -= deltaTime * (qteTimer / qteTime);
if (qteTimer < 0.0f) qteTimer = qteTime;
}
} }
else else
{ {
@@ -270,6 +302,26 @@ namespace Barotrauma.Items.Components
progressBar.BarSize = item.Condition / defaultMaxCondition; progressBar.BarSize = item.Condition / defaultMaxCondition;
progressBar.Color = ToolBox.GradientLerp(progressBar.BarSize, GUI.Style.Red, GUI.Style.Orange, GUI.Style.Green); progressBar.Color = ToolBox.GradientLerp(progressBar.BarSize, GUI.Style.Red, GUI.Style.Orange, GUI.Style.Green);
Rectangle sliderRect = progressBar.GetSliderRect(1.0f);
Color qteSliderColor = Color.White;
if (qteCooldown > 0.0f)
{
qteSliderColor = qteSuccess ? GUI.Style.Green : GUI.Style.Red * 0.5f;
progressBar.Color = ToolBox.GradientLerp(qteCooldown / qteCooldownTime, progressBar.Color, qteSliderColor, Color.White);
}
else
{
if (qteTimer / qteTime <= item.Condition / item.MaxCondition)
{
qteSliderColor = Color.Lerp(qteSliderColor, GUI.Style.Green, 0.5f);
}
}
progressBar.Parent.Parent.Parent.DrawManually(spriteBatch, true);
GUI.DrawRectangle(spriteBatch,
new Rectangle(sliderRect.X + (int)((qteTimer / qteTime) * sliderRect.Width), sliderRect.Y - 5, 2, sliderRect.Height + 10),
qteSliderColor, true);
if (item.Condition > defaultMaxCondition) if (item.Condition > defaultMaxCondition)
{ {
float extraCondition = item.MaxCondition * (item.MaxRepairConditionMultiplier - 1.0f); float extraCondition = item.MaxCondition * (item.MaxRepairConditionMultiplier - 1.0f);
@@ -282,7 +334,7 @@ namespace Barotrauma.Items.Components
progressBarOverlayText.Visible = false; progressBarOverlayText.Visible = false;
} }
RepairButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Repair)) && !item.IsFullCondition && IsBelowRepairThreshold; RepairButton.Enabled = (currentFixerAction == FixActions.None || CurrentFixer == character) && !item.IsFullCondition;
RepairButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Repair) ? RepairButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Repair) ?
repairButtonText : repairButtonText :
repairingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1); repairingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
@@ -352,6 +404,29 @@ namespace Barotrauma.Items.Components
repairSoundChannel = null; repairSoundChannel = null;
} }
private void QTEAction()
{
if (currentFixerAction == FixActions.Repair)
{
qteSuccess = qteCooldown <= 0.0f && qteTimer / qteTime <= item.Condition / item.MaxCondition;
}
else
{
return;
}
if (!GameMain.IsMultiplayer) { RepairBoost(qteSuccess); }
SoundPlayer.PlayUISound(qteSuccess ? GUISoundType.IncreaseQuantity : GUISoundType.DecreaseQuantity);
//on failure during cooldown reset cursor to beginning
if (!qteSuccess && qteCooldown > 0.0f) { qteTimer = qteTime; }
qteCooldown = qteCooldownTime;
//this will be set on button down so we can reset it here
requestStartFixAction = FixActions.None;
item.CreateClientEvent(this);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
deteriorationTimer = msg.ReadSingle(); deteriorationTimer = msg.ReadSingle();
@@ -363,11 +438,13 @@ namespace Barotrauma.Items.Components
currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2); currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
CurrentFixer = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null; CurrentFixer = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null;
item.MaxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(CurrentFixer); item.MaxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(CurrentFixer);
repairBoost = msg.ReadSingle();
} }
public void ClientWrite(IWriteMessage msg, object[] extraData = null) public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{ {
msg.WriteRangedInteger((int)requestStartFixAction, 0, 2); msg.WriteRangedInteger((int)requestStartFixAction, 0, 2);
msg.Write(qteSuccess);
} }
} }
} }

View File

@@ -26,12 +26,7 @@ namespace Barotrauma.Items.Components
AutoHideScrollBar = false AutoHideScrollBar = false
}; };
// Create fillerBlock to cover historyBox so new values appear at the bottom of historyBox CreateFillerBlock();
// This could be removed if GUIListBox supported aligning its children
fillerBlock = new GUITextBlock(new RectTransform(new Vector2(1, 1), historyBox.Content.RectTransform, anchor: Anchor.TopCenter), string.Empty)
{
CanBeFocused = false
};
new GUIFrame(new RectTransform(new Vector2(0.9f, 0.01f), layoutGroup.RectTransform), style: "HorizontalLine"); new GUIFrame(new RectTransform(new Vector2(0.9f, 0.01f), layoutGroup.RectTransform), style: "HorizontalLine");
@@ -55,6 +50,16 @@ namespace Barotrauma.Items.Components
}; };
} }
// Create fillerBlock to cover historyBox so new values appear at the bottom of historyBox
// This could be removed if GUIListBox supported aligning its children
public void CreateFillerBlock()
{
fillerBlock = new GUITextBlock(new RectTransform(new Vector2(1, 1), historyBox.Content.RectTransform, anchor: Anchor.TopCenter), string.Empty)
{
CanBeFocused = false
};
}
private void SendOutput(string input) private void SendOutput(string input)
{ {
if (input.Length > MaxMessageLength) if (input.Length > MaxMessageLength)
@@ -130,7 +135,12 @@ namespace Barotrauma.Items.Components
public void ClientWrite(IWriteMessage msg, object[] extraData = null) public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{ {
msg.Write((string)extraData[2]); if (extraData is null) { return; }
if (extraData[2] is string str)
{
msg.Write(str);
}
} }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)

View File

@@ -160,7 +160,7 @@ namespace Barotrauma.Items.Components
{ {
errorMsg += "\nTrying to dock the submarine to itself."; errorMsg += "\nTrying to dock the submarine to itself.";
} }
GameAnalyticsManager.AddErrorEventOnce("DockingPort.ClientRead:JointNotCreated", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("DockingPort.ClientRead:JointNotCreated", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
} }
if (isLocked) if (isLocked)

View File

@@ -1445,7 +1445,7 @@ namespace Barotrauma
#else #else
if (GameSettings.VerboseLogging) { DebugConsole.ThrowError(errorMsg); } if (GameSettings.VerboseLogging) { DebugConsole.ThrowError(errorMsg); }
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("Item.ClientReadPosition:nophysicsbody", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("Item.ClientReadPosition:nophysicsbody", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
@@ -1586,7 +1586,7 @@ namespace Barotrauma
string errorMsg = "Failed to spawn item, prefab not found (name: " + (itemName ?? "null") + ", identifier: " + (itemIdentifier ?? "null") + ")"; string errorMsg = "Failed to spawn item, prefab not found (name: " + (itemName ?? "null") + ", identifier: " + (itemIdentifier ?? "null") + ")";
errorMsg += "\n" + string.Join(", ", GameMain.Config.AllEnabledPackages.Select(cp => cp.Name)); errorMsg += "\n" + string.Join(", ", GameMain.Config.AllEnabledPackages.Select(cp => cp.Name));
GameAnalyticsManager.AddErrorEventOnce("Item.ReadSpawnData:PrefabNotFound" + (itemName ?? "null") + (itemIdentifier ?? "null"), GameAnalyticsManager.AddErrorEventOnce("Item.ReadSpawnData:PrefabNotFound" + (itemName ?? "null") + (itemIdentifier ?? "null"),
GameAnalyticsSDK.Net.EGAErrorSeverity.Critical, GameAnalyticsManager.ErrorSeverity.Critical,
errorMsg); errorMsg);
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
return null; return null;
@@ -1607,7 +1607,7 @@ namespace Barotrauma
string errorMsg = string errorMsg =
$"Failed to spawn item \"{(itemIdentifier ?? "null")}\" in the inventory of \"{parentItem.prefab.Identifier} ({parentItem.ID})\" (component index out of range). Index: {itemContainerIndex}, components: {parentItem.components.Count}."; $"Failed to spawn item \"{(itemIdentifier ?? "null")}\" in the inventory of \"{parentItem.prefab.Identifier} ({parentItem.ID})\" (component index out of range). Index: {itemContainerIndex}, components: {parentItem.components.Count}.";
GameAnalyticsManager.AddErrorEventOnce("Item.ReadSpawnData:ContainerIndexOutOfRange" + (itemName ?? "null") + (itemIdentifier ?? "null"), GameAnalyticsManager.AddErrorEventOnce("Item.ReadSpawnData:ContainerIndexOutOfRange" + (itemName ?? "null") + (itemIdentifier ?? "null"),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
errorMsg); errorMsg);
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
inventory = parentItem.GetComponent<ItemContainer>()?.Inventory; inventory = parentItem.GetComponent<ItemContainer>()?.Inventory;

View File

@@ -98,7 +98,7 @@ namespace Barotrauma
private IEnumerable<CoroutineStatus> DimLight(LightSource light) private IEnumerable<CoroutineStatus> DimLight(LightSource light)
{ {
float currBrightness = 1.0f; float currBrightness = 1.0f;
while (light.Color.A > 0.0f && flashDuration > 0.0f) while (light.Color.A > 0.0f && flashDuration > 0.0f && currBrightness > 0.0f)
{ {
light.Color = new Color(light.Color.R, light.Color.G, light.Color.B, (byte)(currBrightness * 255)); light.Color = new Color(light.Color.R, light.Color.G, light.Color.B, (byte)(currBrightness * 255));
currBrightness -= 1.0f / flashDuration * CoroutineManager.DeltaTime; currBrightness -= 1.0f / flashDuration * CoroutineManager.DeltaTime;

View File

@@ -92,7 +92,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Invalid left normal"); DebugConsole.ThrowError("Invalid left normal");
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidLeftNormal:" + level.Seed, GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidLeftNormal:" + level.Seed,
GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, GameAnalyticsManager.ErrorSeverity.Warning,
"Invalid left normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + leftNormal + ", seed: " + level.Seed + ")"); "Invalid left normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + leftNormal + ", seed: " + level.Seed + ")");
if (cell.Body != null) if (cell.Body != null)
@@ -127,7 +127,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Invalid right normal"); DebugConsole.ThrowError("Invalid right normal");
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidRightNormal:" + level.Seed, GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidRightNormal:" + level.Seed,
GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, GameAnalyticsManager.ErrorSeverity.Warning,
"Invalid right normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + rightNormal + ", seed: " + level.Seed + ")"); "Invalid right normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + rightNormal + ", seed: " + level.Seed + ")");
if (cell.Body != null) if (cell.Body != null)

View File

@@ -497,11 +497,13 @@ namespace Barotrauma.Lights
return true; return true;
} }
private readonly Dictionary<Hull, Rectangle> visibleHulls = new Dictionary<Hull, Rectangle>();
private Dictionary<Hull, Rectangle> GetVisibleHulls(Camera cam) private Dictionary<Hull, Rectangle> GetVisibleHulls(Camera cam)
{ {
Dictionary<Hull, Rectangle> visibleHulls = new Dictionary<Hull, Rectangle>(); visibleHulls.Clear();
foreach (Hull hull in Hull.hullList) foreach (Hull hull in Hull.hullList)
{ {
if (hull.HiddenInGame) { continue; }
var drawRect = var drawRect =
hull.Submarine == null ? hull.Submarine == null ?
hull.Rect : hull.Rect :

View File

@@ -536,7 +536,7 @@ namespace Barotrauma
invalidMessage = true; invalidMessage = true;
string errorMsg = $"Error while reading a network event for the structure \"{Name} ({ID})\". Section count does not match (server: {sectionCount} client: {Sections.Length})"; string errorMsg = $"Error while reading a network event for the structure \"{Name} ({ID})\". Section count does not match (server: {sectionCount} client: {Sections.Length})";
DebugConsole.NewMessage(errorMsg, Color.Red); DebugConsole.NewMessage(errorMsg, Color.Red);
GameAnalyticsManager.AddErrorEventOnce("Structure.ClientRead:SectionCountMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("Structure.ClientRead:SectionCountMismatch", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
} }
for (int i = 0; i < sectionCount; i++) for (int i = 0; i < sectionCount; i++)

View File

@@ -98,7 +98,7 @@ namespace Barotrauma
{ {
string errorMsg = "Error when loading round sound (" + element + ") - file path not set"; string errorMsg = "Error when loading round sound (" + element + ") - file path not set";
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Submarine.LoadRoundSound:FilePathEmpty" + element.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace()); GameAnalyticsManager.AddErrorEventOnce("Submarine.LoadRoundSound:FilePathEmpty" + element.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
return null; return null;
} }
@@ -124,7 +124,7 @@ namespace Barotrauma
{ {
string errorMsg = "Failed to load sound file \"" + filename + "\"."; string errorMsg = "Failed to load sound file \"" + filename + "\".";
DebugConsole.ThrowError(errorMsg, e); DebugConsole.ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("Submarine.LoadRoundSound:FileNotFound" + filename, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace()); GameAnalyticsManager.AddErrorEventOnce("Submarine.LoadRoundSound:FileNotFound" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
return null; return null;
} }
} }
@@ -148,7 +148,7 @@ namespace Barotrauma
{ {
string errorMsg = "Failed to load sound file \"" + roundSound.Filename + "\"."; string errorMsg = "Failed to load sound file \"" + roundSound.Filename + "\".";
DebugConsole.ThrowError(errorMsg, e); DebugConsole.ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("Submarine.LoadRoundSound:FileNotFound" + roundSound.Filename, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace()); GameAnalyticsManager.AddErrorEventOnce("Submarine.LoadRoundSound:FileNotFound" + roundSound.Filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
return; return;
} }
} }
@@ -183,13 +183,14 @@ namespace Barotrauma
visibleSubs.Clear(); visibleSubs.Clear();
foreach (Submarine sub in Loaded) foreach (Submarine sub in Loaded)
{ {
if (sub.WorldPosition.Y < Level.MaxEntityDepth) continue; if (sub.WorldPosition.Y < Level.MaxEntityDepth) { continue; }
int margin = 500;
Rectangle worldBorders = new Rectangle( Rectangle worldBorders = new Rectangle(
sub.Borders.X + (int)sub.WorldPosition.X - 500, sub.VisibleBorders.X + (int)sub.WorldPosition.X - margin,
sub.Borders.Y + (int)sub.WorldPosition.Y + 500, sub.VisibleBorders.Y + (int)sub.WorldPosition.Y + margin,
sub.Borders.Width + 1000, sub.VisibleBorders.Width + margin * 2,
sub.Borders.Height + 1000); sub.VisibleBorders.Height + margin * 2);
if (RectsOverlap(worldBorders, cam.WorldView)) if (RectsOverlap(worldBorders, cam.WorldView))
{ {

View File

@@ -26,7 +26,7 @@ namespace Barotrauma
catch (Exception e) catch (Exception e)
{ {
DebugConsole.ThrowError("Loading the preview image of the submarine \"" + Name + "\" failed. The file may be corrupted.", e); DebugConsole.ThrowError("Loading the preview image of the submarine \"" + Name + "\" failed. The file may be corrupted.", e);
GameAnalyticsManager.AddErrorEventOnce("Submarine..ctor:PreviewImageLoadingFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.AddErrorEventOnce("Submarine..ctor:PreviewImageLoadingFailed", GameAnalyticsManager.ErrorSeverity.Error,
"Loading the preview image of the submarine \"" + Name + "\" failed. The file may be corrupted."); "Loading the preview image of the submarine \"" + Name + "\" failed. The file may be corrupted.");
PreviewImage = null; PreviewImage = null;
} }

View File

@@ -649,7 +649,7 @@ namespace Barotrauma.Networking
{ {
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace.CleanupStackTrace(); errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace.CleanupStackTrace();
} }
GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + e.TargetSite.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
DebugConsole.ThrowError("Error while reading a message from server.", e); DebugConsole.ThrowError("Error while reading a message from server.", e);
new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", new string[2] { "[message]", "[targetsite]" }, new string[2] { e.Message, e.TargetSite.ToString() })); new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", new string[2] { "[message]", "[targetsite]" }, new string[2] { e.Message, e.TargetSite.ToString() }));
Disconnect(); Disconnect();
@@ -779,7 +779,7 @@ namespace Barotrauma.Networking
#if DEBUG #if DEBUG
DebugConsole.ThrowError("Error while reading an ingame update message from server.", e); DebugConsole.ThrowError("Error while reading an ingame update message from server.", e);
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadDataMessage:ReadIngameUpdate", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadDataMessage:ReadIngameUpdate", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw; throw;
} }
break; break;
@@ -791,7 +791,7 @@ namespace Barotrauma.Networking
errorMsg += "\n" + Environment.StackTrace.CleanupStackTrace(); errorMsg += "\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce( GameAnalyticsManager.AddErrorEventOnce(
"GameClient.ReadDataMessage:VoipClientNull", "GameClient.ReadDataMessage:VoipClientNull",
GameMain.Client == null ? GameAnalyticsSDK.Net.EGAErrorSeverity.Error : GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, GameMain.Client == null ? GameAnalyticsManager.ErrorSeverity.Error : GameAnalyticsManager.ErrorSeverity.Warning,
errorMsg); errorMsg);
return; return;
} }
@@ -989,7 +989,7 @@ namespace Barotrauma.Networking
{ {
string errorMsg = "Submarine equality check failed. The submarine loaded at your end doesn't match the one loaded by the server." + string errorMsg = "Submarine equality check failed. The submarine loaded at your end doesn't match the one loaded by the server." +
" There may have been an error in receiving the up-to-date submarine file from the server."; " There may have been an error in receiving the up-to-date submarine file from the server.";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:SubsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:SubsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg); throw new Exception(errorMsg);
} }
@@ -1010,7 +1010,7 @@ namespace Barotrauma.Networking
if (!GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier).OrderBy(id => id).SequenceEqual(serverMissionIdentifiers.OrderBy(id => id))) if (!GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier).OrderBy(id => id).SequenceEqual(serverMissionIdentifiers.OrderBy(id => id)))
{ {
string errorMsg = $"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server (server: {string.Join(", ", serverMissionIdentifiers)}, client: {string.Join(", ", GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier))})"; string errorMsg = $"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server (server: {string.Join(", ", serverMissionIdentifiers)}, client: {string.Join(", ", GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier))})";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg); throw new Exception(errorMsg);
} }
GameMain.GameSession.EnforceMissionOrder(serverMissionIdentifiers); GameMain.GameSession.EnforceMissionOrder(serverMissionIdentifiers);
@@ -1031,7 +1031,7 @@ namespace Barotrauma.Networking
", seed: " + Level.Loaded.Seed + ", seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" + ", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" +
", mirrored: " + Level.Loaded.Mirrored + ")."; ", mirrored: " + Level.Loaded.Mirrored + ").";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg); throw new Exception(errorMsg);
} }
else else
@@ -1047,7 +1047,7 @@ namespace Barotrauma.Networking
", seed: " + Level.Loaded.Seed + ", seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" + ", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" +
", mirrored: " + Level.Loaded.Mirrored + ")."; ", mirrored: " + Level.Loaded.Mirrored + ").";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg); throw new Exception(errorMsg);
} }
} }
@@ -1117,8 +1117,8 @@ namespace Barotrauma.Networking
{ {
GameAnalyticsManager.AddErrorEventOnce( GameAnalyticsManager.AddErrorEventOnce(
"GameClient.HandleDisconnectMessage", "GameClient.HandleDisconnectMessage",
GameAnalyticsSDK.Net.EGAErrorSeverity.Debug, GameAnalyticsManager.ErrorSeverity.Debug,
"Client received a disconnect message. Reason: " + disconnectReason.ToString() + ", message: " + disconnectMsg); "Client received a disconnect message. Reason: " + disconnectReason.ToString());
} }
if (disconnectReason == DisconnectReason.ServerFull) if (disconnectReason == DisconnectReason.ServerFull)
@@ -1532,7 +1532,7 @@ namespace Barotrauma.Networking
gameStarted = true; gameStarted = true;
GameMain.NetLobbyScreen.Select(); GameMain.NetLobbyScreen.Select();
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:FailedToSelectSub" + subName, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:FailedToSelectSub" + subName, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
roundInitStatus = RoundInitStatus.Interrupted; roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure; yield return CoroutineStatus.Failure;
} }
@@ -1544,7 +1544,7 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.Select(); GameMain.NetLobbyScreen.Select();
string errorMsg = "Failed to select shuttle \"" + shuttleName + "\" (hash: " + shuttleHash + ")."; string errorMsg = "Failed to select shuttle \"" + shuttleName + "\" (hash: " + shuttleHash + ").";
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:FailedToSelectShuttle" + shuttleName, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:FailedToSelectShuttle" + shuttleName, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
roundInitStatus = RoundInitStatus.Interrupted; roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure; yield return CoroutineStatus.Failure;
} }
@@ -2116,9 +2116,6 @@ namespace Barotrauma.Networking
bool autoRestartEnabled = inc.ReadBoolean(); bool autoRestartEnabled = inc.ReadBoolean();
float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f; float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f;
bool radiationEnabled = inc.ReadBoolean();
byte maxMissionCount = inc.ReadByte();
//ignore the message if we already a more up-to-date one //ignore the message if we already a more up-to-date one
//or if we're still waiting for the initial update //or if we're still waiting for the initial update
if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) && if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) &&
@@ -2168,22 +2165,15 @@ namespace Barotrauma.Networking
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "campaign")) if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "campaign"))
{ {
GameMain.NetLobbyScreen.CampaignSubmarines.Add(sub); GameMain.NetLobbyScreen.CampaignSubmarines.Add(sub);
} }
}
if (HasPermission(ClientPermissions.ManageCampaign) && !gameStarted && GameMain.NetLobbyScreen?.CampaignSetupUI != null)
{
GameMain.NetLobbyScreen.CampaignSetupUI.RefreshMultiplayerCampaignSubUI(GameMain.NetLobbyScreen.CampaignSubmarines);
} }
} }
GameMain.NetLobbyScreen.SetAllowSpectating(allowSpectating); GameMain.NetLobbyScreen.SetAllowSpectating(allowSpectating);
GameMain.NetLobbyScreen.LevelSeed = levelSeed; GameMain.NetLobbyScreen.LevelSeed = levelSeed;
GameMain.NetLobbyScreen.SetLevelDifficulty(levelDifficulty); GameMain.NetLobbyScreen.SetLevelDifficulty(levelDifficulty);
GameMain.NetLobbyScreen.SetRadiationEnabled(radiationEnabled);
GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode); GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode);
GameMain.NetLobbyScreen.SetBotCount(botCount); GameMain.NetLobbyScreen.SetBotCount(botCount);
GameMain.NetLobbyScreen.SetMaxMissionCount(maxMissionCount);
GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer); GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer);
serverSettings.VoiceChatEnabled = voiceChatEnabled; serverSettings.VoiceChatEnabled = voiceChatEnabled;
@@ -2355,7 +2345,7 @@ namespace Barotrauma.Networking
{ {
errorLines.Add("[" + DebugConsole.Messages[i].Time + "] " + DebugConsole.Messages[i].Text); errorLines.Add("[" + DebugConsole.Messages[i].Time + "] " + DebugConsole.Messages[i].Text);
} }
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadInGameUpdate", GameAnalyticsSDK.Net.EGAErrorSeverity.Critical, string.Join("\n", errorLines)); GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadInGameUpdate", GameAnalyticsManager.ErrorSeverity.Critical, string.Join("\n", errorLines));
DebugConsole.ThrowError("Writing object data to \"networkerror_data.log\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues"); DebugConsole.ThrowError("Writing object data to \"networkerror_data.log\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues");
@@ -2591,29 +2581,29 @@ namespace Barotrauma.Networking
subElement.ToolTip = newSub.Description; subElement.ToolTip = newSub.Description;
} }
if (GameMain.NetLobbyScreen.FailedSelectedSub != null && if (GameMain.NetLobbyScreen.FailedSelectedSub.HasValue &&
GameMain.NetLobbyScreen.FailedSelectedSub.First == newSub.Name && GameMain.NetLobbyScreen.FailedSelectedSub.Value.Name == newSub.Name &&
GameMain.NetLobbyScreen.FailedSelectedSub.Second == newSub.MD5Hash.Hash) GameMain.NetLobbyScreen.FailedSelectedSub.Value.Hash == newSub.MD5Hash.Hash)
{ {
GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.Hash, GameMain.NetLobbyScreen.SubList); GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.Hash, GameMain.NetLobbyScreen.SubList);
} }
if (GameMain.NetLobbyScreen.FailedSelectedShuttle != null && if (GameMain.NetLobbyScreen.FailedSelectedShuttle.HasValue &&
GameMain.NetLobbyScreen.FailedSelectedShuttle.First == newSub.Name && GameMain.NetLobbyScreen.FailedSelectedShuttle.Value.Name == newSub.Name &&
GameMain.NetLobbyScreen.FailedSelectedShuttle.Second == newSub.MD5Hash.Hash) GameMain.NetLobbyScreen.FailedSelectedShuttle.Value.Hash == newSub.MD5Hash.Hash)
{ {
GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.Hash, GameMain.NetLobbyScreen.ShuttleList.ListBox); GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.Hash, GameMain.NetLobbyScreen.ShuttleList.ListBox);
} }
Pair<string, string> failedCampaignSub = GameMain.NetLobbyScreen.FailedCampaignSubs.Find(s => s.First == newSub.Name && s.Second == newSub.MD5Hash.Hash); NetLobbyScreen.FailedSubInfo failedCampaignSub = GameMain.NetLobbyScreen.FailedCampaignSubs.Find(s => s.Name == newSub.Name && s.Hash == newSub.MD5Hash.Hash);
if (failedCampaignSub != null) if (failedCampaignSub != default)
{ {
GameMain.NetLobbyScreen.CampaignSubmarines.Add(newSub); GameMain.NetLobbyScreen.CampaignSubmarines.Add(newSub);
GameMain.NetLobbyScreen.FailedCampaignSubs.Remove(failedCampaignSub); GameMain.NetLobbyScreen.FailedCampaignSubs.Remove(failedCampaignSub);
} }
Pair<string, string> failedOwnedSub = GameMain.NetLobbyScreen.FailedOwnedSubs.Find(s => s.First == newSub.Name && s.Second == newSub.MD5Hash.Hash); NetLobbyScreen.FailedSubInfo failedOwnedSub = GameMain.NetLobbyScreen.FailedOwnedSubs.Find(s => s.Name == newSub.Name && s.Hash == newSub.MD5Hash.Hash);
if (failedOwnedSub != null) if (failedOwnedSub != default)
{ {
GameMain.NetLobbyScreen.ServerOwnedSubmarines.Add(newSub); GameMain.NetLobbyScreen.ServerOwnedSubmarines.Add(newSub);
GameMain.NetLobbyScreen.FailedOwnedSubs.Remove(failedOwnedSub); GameMain.NetLobbyScreen.FailedOwnedSubs.Remove(failedOwnedSub);
@@ -2946,7 +2936,6 @@ namespace Barotrauma.Networking
IWriteMessage msg = new WriteOnlyMessage(); IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND); msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
msg.Write((UInt16)ClientPermissions.SelectSub); msg.Write((UInt16)ClientPermissions.SelectSub);
msg.Write(false);
msg.Write(isShuttle); msg.WritePadBits(); msg.Write(isShuttle); msg.WritePadBits();
msg.Write((UInt16)subIndex); msg.Write((UInt16)subIndex);
msg.Write((byte)ServerNetObject.END_OF_MESSAGE); msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
@@ -2954,23 +2943,6 @@ namespace Barotrauma.Networking
clientPeer.Send(msg, DeliveryMethod.Reliable); clientPeer.Send(msg, DeliveryMethod.Reliable);
} }
/// <summary>
/// Tell the server to add / remove a purchasable submarine (permission required)
/// </summary>
public void RequestCampaignSub(SubmarineInfo sub, bool add)
{
if (!HasPermission(ClientPermissions.SelectSub) || sub == null) return;
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
msg.Write((UInt16)ClientPermissions.SelectSub);
msg.Write(true);
msg.Write(sub.EqualityCheckVal);
msg.Write(add);
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
clientPeer.Send(msg, DeliveryMethod.Reliable);
}
/// <summary> /// <summary>
/// Tell the server to select a mode (permission required) /// Tell the server to select a mode (permission required)
/// </summary> /// </summary>

View File

@@ -233,7 +233,7 @@ namespace Barotrauma.Networking
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
//TODO: force the BitPosition to correct place? Having some entity in a potentially incorrect state is not as bad as a desync kick //TODO: force the BitPosition to correct place? Having some entity in a potentially incorrect state is not as bad as a desync kick
//msg.BitPosition = (int)(msgPosition + msgLength * 8); //msg.BitPosition = (int)(msgPosition + msgLength * 8);
@@ -252,7 +252,7 @@ namespace Barotrauma.Networking
DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e); DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(), GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
msg.BitPosition = (int)(msgPosition + msgLength * 8); msg.BitPosition = (int)(msgPosition + msgLength * 8);
msg.ReadPadBits(); msg.ReadPadBits();
} }

View File

@@ -115,13 +115,11 @@ namespace Barotrauma.Networking
{ {
if (!isActive) { return; } if (!isActive) { return; }
byte incByte = inc.ReadByte(); PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
//Console.WriteLine(isCompressed + " " + isConnectionInitializationStep + " " + (int)incByte); //Console.WriteLine(isCompressed + " " + isConnectionInitializationStep + " " + (int)incByte);
if (isConnectionInitializationStep && initializationStep != ConnectionInitialization.Success) if (packetHeader.IsConnectionInitializationStep() && initializationStep != ConnectionInitialization.Success)
{ {
ReadConnectionInitializationStep(new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false)); ReadConnectionInitializationStep(new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false));
} }
@@ -133,7 +131,7 @@ namespace Barotrauma.Networking
initializationStep = ConnectionInitialization.Success; initializationStep = ConnectionInitialization.Success;
} }
UInt16 length = inc.ReadUInt16(); UInt16 length = inc.ReadUInt16();
IReadMessage msg = new ReadOnlyMessage(inc.Data, isCompressed, inc.PositionInBytes, length, ServerConnection); IReadMessage msg = new ReadOnlyMessage(inc.Data, packetHeader.IsCompressed(), inc.PositionInBytes, length, ServerConnection);
OnMessageReceived?.Invoke(msg); OnMessageReceived?.Invoke(msg);
} }
} }

View File

@@ -112,33 +112,28 @@ namespace Barotrauma.Networking
NetworkConnection.TimeoutThresholdInGame : NetworkConnection.TimeoutThresholdInGame :
NetworkConnection.TimeoutThreshold; NetworkConnection.TimeoutThreshold;
byte incByte = data[0]; PacketHeader packetHeader = (PacketHeader)data[0];
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0;
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;
if (!isServerMessage) { return; } if (!packetHeader.IsServerMessage()) { return; }
if (isConnectionInitializationStep) if (packetHeader.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 - (1 + 8), ServerConnection);
if (initializationStep != ConnectionInitialization.Success) if (initializationStep != ConnectionInitialization.Success)
{ {
incomingInitializationMessages.Add(inc); incomingInitializationMessages.Add(inc);
} }
} }
else if (isHeartbeatMessage) else if (packetHeader.IsHeartbeatMessage())
{ {
return; //TODO: implement heartbeats return; //TODO: implement heartbeats
} }
else if (isDisconnectMessage) else if (packetHeader.IsDisconnectMessage())
{ {
IReadMessage inc = new ReadOnlyMessage(data, false, 1, dataLength - 1, ServerConnection); IReadMessage inc = new ReadOnlyMessage(data, false, 1, dataLength - 1, ServerConnection);
string msg = inc.ReadString(); string msg = inc.ReadString();
@@ -147,10 +142,9 @@ namespace Barotrauma.Networking
} }
else else
{ {
UInt16 length = data[1]; UInt16 length = Lidgren.Network.NetBitWriter.ReadUInt16(data, 16, 8);
length |= (UInt16)(((UInt32)data[2]) << 8);
IReadMessage inc = new ReadOnlyMessage(data, isCompressed, 3, length, ServerConnection); IReadMessage inc = new ReadOnlyMessage(data, packetHeader.IsCompressed(), 3, length, ServerConnection);
incomingDataMessages.Add(inc); incomingDataMessages.Add(inc);
} }
} }

View File

@@ -147,18 +147,13 @@ namespace Barotrauma.Networking
DeliveryMethod deliveryMethod = (DeliveryMethod)data[0]; DeliveryMethod deliveryMethod = (DeliveryMethod)data[0];
byte incByte = data[1]; PacketHeader packetHeader = (PacketHeader)data[1];
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0;
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;
if (!remotePeer.Authenticated & !remotePeer.Authenticating && isConnectionInitializationStep) if (!remotePeer.Authenticated & !remotePeer.Authenticating && packetHeader.IsConnectionInitializationStep())
{ {
remotePeer.DisconnectTime = null; remotePeer.DisconnectTime = null;
IReadMessage authMsg = new ReadOnlyMessage(data, isCompressed, 2, dataLength - 2, null); IReadMessage authMsg = new ReadOnlyMessage(data, packetHeader.IsCompressed(), 2, dataLength - 2, null);
ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte(); ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte();
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
{ {
@@ -242,17 +237,11 @@ namespace Barotrauma.Networking
int p2pDataStart = inc.BytePosition; int p2pDataStart = inc.BytePosition;
byte incByte = inc.ReadByte(); PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0;
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;
if (recipientSteamId != selfSteamID) if (recipientSteamId != selfSteamID)
{ {
if (!isServerMessage) if (!packetHeader.IsServerMessage())
{ {
DebugConsole.ThrowError("Received non-server message meant for remote peer"); DebugConsole.ThrowError("Received non-server message meant for remote peer");
return; return;
@@ -262,7 +251,7 @@ namespace Barotrauma.Networking
if (peer == null) { return; } if (peer == null) { return; }
if (isDisconnectMessage) if (packetHeader.IsDisconnectMessage())
{ {
DisconnectPeer(peer, inc.ReadString()); DisconnectPeer(peer, inc.ReadString());
return; return;
@@ -273,8 +262,8 @@ namespace Barotrauma.Networking
{ {
case DeliveryMethod.Reliable: case DeliveryMethod.Reliable:
case DeliveryMethod.ReliableOrdered: case DeliveryMethod.ReliableOrdered:
//the documentation seems to suggest that the Reliable send type //the documentation seems to suggest that the
//enforces packet order (TODO: verify) //Reliable send type enforces packet order
sendType = Steamworks.P2PSend.Reliable; sendType = Steamworks.P2PSend.Reliable;
break; break;
default: default:
@@ -284,17 +273,31 @@ namespace Barotrauma.Networking
byte[] p2pData; byte[] p2pData;
if (isConnectionInitializationStep) if (packetHeader.IsConnectionInitializationStep())
{ {
p2pData = new byte[inc.LengthBytes - p2pDataStart + 8]; p2pData = new byte[inc.LengthBytes - p2pDataStart + 8];
p2pData[0] = inc.Buffer[p2pDataStart]; p2pData[0] = inc.Buffer[p2pDataStart];
Lidgren.Network.NetBitWriter.WriteUInt64(SteamManager.CurrentLobbyID, 64, p2pData, 8); Lidgren.Network.NetBitWriter.WriteUInt64(SteamManager.CurrentLobbyID, 8 * 8, p2pData, 1 * 8);
Array.Copy(inc.Buffer, p2pDataStart+1, p2pData, 9, inc.LengthBytes - p2pDataStart - 1); Array.Copy(inc.Buffer, p2pDataStart+1, p2pData, 1 + 8, inc.LengthBytes - p2pDataStart - 1);
} }
else else
{ {
p2pData = new byte[inc.LengthBytes - p2pDataStart]; p2pData = new byte[inc.LengthBytes - p2pDataStart];
Array.Copy(inc.Buffer, p2pDataStart, p2pData, 0, p2pData.Length); Array.Copy(inc.Buffer, p2pDataStart, p2pData, 0, p2pData.Length);
if (!packetHeader.IsHeartbeatMessage() && !packetHeader.IsDisconnectMessage())
{
UInt16 length = Lidgren.Network.NetBitWriter.ReadUInt16(p2pData, 16, 8);
if (length > p2pData.Length - 2)
{
string errorMsg = $"Length written in message to send to client is larger than buffer size ({length} > {p2pData.Length - 2})";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce(
"SteamP2POwnerPeerLengthValidationFail",
GameAnalyticsManager.ErrorSeverity.Error,
errorMsg);
}
}
} }
if (p2pData.Length + 4 >= MsgConstants.MTU) if (p2pData.Length + 4 >= MsgConstants.MTU)
@@ -323,21 +326,21 @@ namespace Barotrauma.Networking
} }
else else
{ {
if (isDisconnectMessage) if (packetHeader.IsDisconnectMessage())
{ {
DebugConsole.ThrowError("Received disconnect message from owned server"); DebugConsole.ThrowError("Received disconnect message from owned server");
return; return;
} }
if (!isServerMessage) if (!packetHeader.IsServerMessage())
{ {
DebugConsole.ThrowError("Received non-server message from owned server"); DebugConsole.ThrowError("Received non-server message from owned server");
return; return;
} }
if (isHeartbeatMessage) if (packetHeader.IsHeartbeatMessage())
{ {
return; //timeout is handled by Lidgren, ignore this message return; //no timeout since we're using pipes, ignore this message
} }
if (isConnectionInitializationStep) if (packetHeader.IsConnectionInitializationStep())
{ {
IWriteMessage outMsg = new WriteOnlyMessage(); IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write(selfSteamID); outMsg.Write(selfSteamID);
@@ -358,7 +361,7 @@ namespace Barotrauma.Networking
initializationStep = ConnectionInitialization.Success; initializationStep = ConnectionInitialization.Success;
} }
UInt16 length = inc.ReadUInt16(); UInt16 length = inc.ReadUInt16();
IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, ServerConnection); IReadMessage msg = new ReadOnlyMessage(inc.Buffer, packetHeader.IsCompressed(), inc.BytePosition, length, ServerConnection);
OnMessageReceived?.Invoke(msg); OnMessageReceived?.Invoke(msg);
return; return;

View File

@@ -242,7 +242,8 @@ namespace Barotrauma.Networking
textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea() textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea()
{ {
Data = data, Data = data,
OnClick = GameMain.NetLobbyScreen.SelectPlayer OnClick = GameMain.NetLobbyScreen.SelectPlayer,
OnSecondaryClick = GameMain.NetLobbyScreen.ShowPlayerContextMenu
}); });
} }
} }

View File

@@ -92,8 +92,9 @@ namespace Barotrauma.Networking
} }
} }
public void ClientAdminRead(IReadMessage incMsg) public void ClientAdminRead(IReadMessage incMsg, NetFlags requiredFlags)
{ {
if (!requiredFlags.HasFlag(NetFlags.Properties)) { return; }
int count = incMsg.ReadUInt16(); int count = incMsg.ReadUInt16();
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
@@ -128,8 +129,18 @@ namespace Barotrauma.Networking
{ {
cachedServerListInfo = null; cachedServerListInfo = null;
ServerName = incMsg.ReadString(); NetFlags requiredFlags = (NetFlags)incMsg.ReadByte();
ServerMessageText = incMsg.ReadString();
if (requiredFlags.HasFlag(NetFlags.Name))
{
ServerName = incMsg.ReadString();
}
if (requiredFlags.HasFlag(NetFlags.Message))
{
ServerMessageText = incMsg.ReadString();
}
PlayStyle = (PlayStyle)incMsg.ReadByte();
MaxPlayers = incMsg.ReadByte(); MaxPlayers = incMsg.ReadByte();
HasPassword = incMsg.ReadBoolean(); HasPassword = incMsg.ReadBoolean();
IsPublic = incMsg.ReadBoolean(); IsPublic = incMsg.ReadBoolean();
@@ -139,23 +150,23 @@ namespace Barotrauma.Networking
TickRate = incMsg.ReadRangedInteger(1, 60); TickRate = incMsg.ReadRangedInteger(1, 60);
GameMain.NetworkMember.TickRate = TickRate; GameMain.NetworkMember.TickRate = TickRate;
ReadExtraCargo(incMsg); if (requiredFlags.HasFlag(NetFlags.Properties))
{
ReadExtraCargo(incMsg);
}
ReadHiddenSubs(incMsg); ReadHiddenSubs(incMsg);
GameMain.NetLobbyScreen.UpdateSubVisibility(); GameMain.NetLobbyScreen.UpdateSubVisibility();
Voting.ClientRead(incMsg);
bool isAdmin = incMsg.ReadBoolean(); bool isAdmin = incMsg.ReadBoolean();
incMsg.ReadPadBits(); incMsg.ReadPadBits();
if (isAdmin) if (isAdmin)
{ {
ClientAdminRead(incMsg); ClientAdminRead(incMsg, requiredFlags);
} }
} }
public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? radiationEnabled = null, bool? useRespawnShuttle = null, int maxMissionCount = 0) public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? useRespawnShuttle = null)
{ {
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return; if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
@@ -225,8 +236,6 @@ namespace Barotrauma.Networking
outMsg.Write(autoRestart != null); outMsg.Write(autoRestart != null);
outMsg.Write(autoRestart ?? false); outMsg.Write(autoRestart ?? false);
outMsg.Write(radiationEnabled ?? RadiationEnabled);
outMsg.Write((byte)maxMissionCount + 1);
outMsg.WritePadBits(); outMsg.WritePadBits();
} }
@@ -711,7 +720,6 @@ namespace Barotrauma.Networking
RelativeSpacing = 0.05f RelativeSpacing = 0.05f
}; };
if (ip.InventoryIcon != null || ip.sprite != null) if (ip.InventoryIcon != null || ip.sprite != null)
{ {
GUIImage img = new GUIImage(new RectTransform(new Point(itemFrame.Rect.Height), itemFrame.RectTransform), GUIImage img = new GUIImage(new RectTransform(new Point(itemFrame.Rect.Height), itemFrame.RectTransform),
@@ -734,7 +742,7 @@ namespace Barotrauma.Networking
GUINumberInput.NumberType.Int, textAlignment: Alignment.CenterLeft) GUINumberInput.NumberType.Int, textAlignment: Alignment.CenterLeft)
{ {
MinValueInt = 0, MinValueInt = 0,
MaxValueInt = 100, MaxValueInt = MaxExtraCargoItemsOfType,
IntValue = cargoVal IntValue = cargoVal
}; };
amountInput.OnValueChanged += (numberInput) => amountInput.OnValueChanged += (numberInput) =>
@@ -742,16 +750,26 @@ namespace Barotrauma.Networking
if (ExtraCargo.ContainsKey(ip)) if (ExtraCargo.ContainsKey(ip))
{ {
ExtraCargo[ip] = numberInput.IntValue; ExtraCargo[ip] = numberInput.IntValue;
if (numberInput.IntValue <= 0) ExtraCargo.Remove(ip); if (numberInput.IntValue <= 0) { ExtraCargo.Remove(ip); }
} }
else else if (ExtraCargo.Keys.Count() < MaxExtraCargoItemTypes)
{ {
ExtraCargo.Add(ip, numberInput.IntValue); ExtraCargo.Add(ip, numberInput.IntValue);
} }
numberInput.IntValue = ExtraCargo.ContainsKey(ip) ? ExtraCargo[ip] : 0;
CoroutineManager.Invoke(() =>
{
foreach (var child in cargoFrame.Content.GetAllChildren())
{
if (child.GetChild<GUINumberInput>() is GUINumberInput otherNumberInput)
{
otherNumberInput.PlusButton.Enabled = ExtraCargo.Keys.Count() < MaxExtraCargoItemTypes && otherNumberInput.IntValue < otherNumberInput.MaxValueInt;
}
}
}, 0.0f);
}; };
} }
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// antigriefing // antigriefing
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
@@ -913,6 +931,7 @@ namespace Barotrauma.Networking
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
Whitelist.CreateWhiteListFrame(settingsTabs[(int)SettingsTab.Whitelist]); Whitelist.CreateWhiteListFrame(settingsTabs[(int)SettingsTab.Whitelist]);
Whitelist.localEnabled = Whitelist.Enabled;
} }
private void CreateLabeledSlider(GUIComponent parent, string labelTag, out GUIScrollBar slider, out GUITextBlock label) private void CreateLabeledSlider(GUIComponent parent, string labelTag, out GUIScrollBar slider, out GUITextBlock label)

View File

@@ -147,7 +147,14 @@ namespace Barotrauma.Steam
if (currentLobby == null) if (currentLobby == null)
{ {
DebugConsole.ThrowError("Failed to create Steam lobby"); DebugConsole.ThrowError("Failed to create Steam lobby: returned lobby was null");
lobbyState = LobbyState.NotConnected;
return;
}
if (currentLobby.Value.Result != Steamworks.Result.OK)
{
DebugConsole.ThrowError($"Failed to create Steam lobby: result was {currentLobby.Value.Result}");
lobbyState = LobbyState.NotConnected; lobbyState = LobbyState.NotConnected;
return; return;
} }
@@ -525,18 +532,6 @@ namespace Barotrauma.Steam
} }
#region Connecting to servers #region Connecting to servers
private static Steamworks.AuthTicket currentTicket = null;
public static Steamworks.AuthTicket GetAuthSessionTicket()
{
if (!isInitialized)
{
return null;
}
currentTicket?.Cancel();
currentTicket = Steamworks.SteamUser.GetAuthSessionTicket();
return currentTicket;
}
public static Steamworks.BeginAuthResult StartAuthSession(byte[] authTicketData, ulong clientSteamID) public static Steamworks.BeginAuthResult StartAuthSession(byte[] authTicketData, ulong clientSteamID)
{ {
@@ -884,9 +879,9 @@ namespace Barotrauma.Steam
catch (Exception e) catch (Exception e)
{ {
string errorMsg = "Failed to save workshop item preview image to \"" + previewImagePath + "\" when creating workshop item staging folder."; string errorMsg = "Failed to save workshop item preview image when creating workshop item staging folder.";
GameAnalyticsManager.AddErrorEventOnce("SteamManager.CreateWorkshopItemStaging:WriteAllBytesFailed" + previewImagePath, GameAnalyticsManager.AddErrorEventOnce("SteamManager.CreateWorkshopItemStaging:WriteAllBytesFailed" + previewImagePath,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + e.Message); GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + e.Message);
} }
return true; return true;
@@ -1434,8 +1429,8 @@ namespace Barotrauma.Steam
"\" not found. Could not combine path (" + (item.Directory ?? "directory name empty") + ")."; "\" not found. Could not combine path (" + (item.Directory ?? "directory name empty") + ").";
DebugConsole.ThrowError(errorMessage); DebugConsole.ThrowError(errorMessage);
GameAnalyticsManager.AddErrorEventOnce("SteamManager.CheckWorkshopItemInstalled:PathCombineException" + item.Title, GameAnalyticsManager.AddErrorEventOnce("SteamManager.CheckWorkshopItemInstalled:PathCombineException" + item.Title,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
errorMessage); "Metadata file for a Workshop item not found. Could not combine path.");
return false; return false;
} }
@@ -1567,8 +1562,8 @@ namespace Barotrauma.Steam
} }
GameAnalyticsManager.AddErrorEventOnce( GameAnalyticsManager.AddErrorEventOnce(
"SteamManager.AutoUpdateWorkshopItems:" + e.Message, "SteamManager.AutoUpdateWorkshopItems:" + e.Message,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
"Failed to autoupdate workshop item \"" + item.Title + "\". " + e.Message + "\n" + e.StackTrace.CleanupStackTrace()); "Failed to autoupdate workshop item. " + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
}); });
} }
} }

View File

@@ -113,7 +113,7 @@ namespace Barotrauma.Networking
UserData = "capturedevicenotfound" UserData = "capturedevicenotfound"
}; };
} }
GameAnalyticsManager.AddErrorEventOnce("Alc.CaptureDeviceOpenFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.AddErrorEventOnce("Alc.CaptureDeviceOpenFailed", GameAnalyticsManager.ErrorSeverity.Error,
"Alc.CaptureDeviceOpen(" + deviceName + ") failed. Error code: " + errorCode); "Alc.CaptureDeviceOpen(" + deviceName + ") failed. Error code: " + errorCode);
GameMain.Config.VoiceSetting = GameSettings.VoiceMode.Disabled; GameMain.Config.VoiceSetting = GameSettings.VoiceMode.Disabled;
Instance?.Dispose(); Instance?.Dispose();

View File

@@ -56,17 +56,12 @@ namespace Barotrauma.Networking
{ {
nameBox.Enabled = box.Selected; nameBox.Enabled = box.Selected;
ipBox.Enabled = box.Selected; ipBox.Enabled = box.Selected;
addNewButton.Enabled = box.Selected; addNewButton.Enabled = box.Selected && !string.IsNullOrEmpty(ipBox.Text) && !string.IsNullOrEmpty(nameBox.Text);
localEnabled = box.Selected; localEnabled = box.Selected;
return true; return true;
} }
}; };
localEnabled = Enabled;
var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), whitelistFrame.RectTransform)); var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), whitelistFrame.RectTransform));
foreach (WhiteListedPlayer wlp in whitelistedPlayers) foreach (WhiteListedPlayer wlp in whitelistedPlayers)
{ {

View File

@@ -136,7 +136,7 @@ namespace Barotrauma.Particles
Prefab = prefab; Prefab = prefab;
} }
public void Emit(float deltaTime, Vector2 position, Hull hullGuess = null, float angle = 0.0f, float particleRotation = 0.0f, float velocityMultiplier = 1.0f, float sizeMultiplier = 1.0f, float amountMultiplier = 1.0f, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, Tuple<Vector2, Vector2> tracerPoints = null) public void Emit(float deltaTime, Vector2 position, Hull hullGuess = null, float angle = 0.0f, float particleRotation = 0.0f, float velocityMultiplier = 1.0f, float sizeMultiplier = 1.0f, float amountMultiplier = 1.0f, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, bool mirrorAngle = false, Tuple<Vector2, Vector2> tracerPoints = null)
{ {
if (GameMain.Client?.MidRoundSyncing ?? false) { return; } if (GameMain.Client?.MidRoundSyncing ?? false) { return; }
@@ -159,7 +159,7 @@ namespace Barotrauma.Particles
for (float z = 0.0f; z < dist; z += Prefab.Properties.EmitAcrossRayInterval) for (float z = 0.0f; z < dist; z += Prefab.Properties.EmitAcrossRayInterval)
{ {
Vector2 pos = tracerPoints.Item1 + dir * z; Vector2 pos = tracerPoints.Item1 + dir * z;
Emit(pos, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, tracerPoints: null); Emit(pos, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, mirrorAngle, tracerPoints: null);
} }
} }
} }
@@ -169,7 +169,7 @@ namespace Barotrauma.Particles
float emitInterval = 1.0f / Prefab.Properties.ParticlesPerSecond; float emitInterval = 1.0f / Prefab.Properties.ParticlesPerSecond;
while (emitTimer > emitInterval) while (emitTimer > emitInterval)
{ {
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, tracerPoints: tracerPoints); Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, mirrorAngle, tracerPoints: tracerPoints);
emitTimer -= emitInterval; emitTimer -= emitInterval;
} }
} }
@@ -183,7 +183,7 @@ namespace Barotrauma.Particles
} }
} }
private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, Tuple<Vector2, Vector2> tracerPoints = null) private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, bool mirrorAngle = false, Tuple<Vector2, Vector2> tracerPoints = null)
{ {
var particlePrefab = overrideParticle ?? Prefab.ParticlePrefab; var particlePrefab = overrideParticle ?? Prefab.ParticlePrefab;
if (particlePrefab == null) { return; } if (particlePrefab == null) { return; }
@@ -191,7 +191,7 @@ namespace Barotrauma.Particles
Vector2 velocity = Vector2.Zero; Vector2 velocity = Vector2.Zero;
if (!MathUtils.NearlyEqual(Prefab.Properties.VelocityMax * velocityMultiplier, 0.0f) || !MathUtils.NearlyEqual(Prefab.Properties.DistanceMax, 0.0f)) if (!MathUtils.NearlyEqual(Prefab.Properties.VelocityMax * velocityMultiplier, 0.0f) || !MathUtils.NearlyEqual(Prefab.Properties.DistanceMax, 0.0f))
{ {
angle += Rand.Range(Prefab.Properties.AngleMinRad, Prefab.Properties.AngleMaxRad); angle += Rand.Range(Prefab.Properties.AngleMinRad, Prefab.Properties.AngleMaxRad) * (mirrorAngle ? -1 : 1);
Vector2 dir = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)); Vector2 dir = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
velocity = dir * Rand.Range(Prefab.Properties.VelocityMin, Prefab.Properties.VelocityMax) * velocityMultiplier; velocity = dir * Rand.Range(Prefab.Properties.VelocityMin, Prefab.Properties.VelocityMax) * velocityMultiplier;
position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax); position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax);

View File

@@ -201,7 +201,7 @@ namespace Barotrauma
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("PhysicsBody.ClientRead:InvalidData" + parentDebugName, GameAnalyticsManager.AddErrorEventOnce("PhysicsBody.ClientRead:InvalidData" + parentDebugName,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
errorMsg); errorMsg);
return null; return null;
} }

View File

@@ -1,11 +1,9 @@
#region Using Statements #region Using Statements
using System; using System;
using System.Collections.Generic;
using Barotrauma.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using GameAnalyticsSDK.Net;
using Barotrauma.Steam; using Barotrauma.Steam;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -245,6 +243,13 @@ namespace Barotrauma
} }
} }
if (GameAnalyticsManager.SendUserStatistics)
{
//send crash report before appending debug console messages (which may contain non-anonymous information)
GameAnalyticsManager.AddErrorEvent(GameAnalyticsManager.ErrorSeverity.Critical, sb.ToString());
GameAnalyticsManager.ShutDown();
}
sb.AppendLine("Last debug messages:"); sb.AppendLine("Last debug messages:");
for (int i = DebugConsole.Messages.Count - 1; i >= 0; i--) for (int i = DebugConsole.Messages.Count - 1; i >= 0; i--)
{ {
@@ -257,11 +262,9 @@ namespace Barotrauma
if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); } if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); }
if (GameSettings.SendUserStatistics) if (GameAnalyticsManager.SendUserStatistics)
{ {
CrashMessageBox("A crash report (\"" + filePath + "\") was saved in the root folder of the game and sent to the developers.", filePath); CrashMessageBox("A crash report (\"" + filePath + "\") was saved in the root folder of the game and sent to the developers.", filePath);
GameAnalytics.AddErrorEvent(EGAErrorSeverity.Critical, crashReport);
GameAnalytics.OnQuit();
} }
else else
{ {

View File

@@ -7,14 +7,10 @@ namespace Barotrauma
{ {
protected readonly GUIComponent newGameContainer, loadGameContainer; protected readonly GUIComponent newGameContainer, loadGameContainer;
protected GUIListBox subList;
protected GUIListBox saveList; protected GUIListBox saveList;
protected List<GUITickBox> subTickBoxes;
protected GUITextBox saveNameBox, seedBox; protected GUITextBox saveNameBox, seedBox;
protected GUILayoutGroup subPreviewContainer;
protected GUIButton loadGameButton; protected GUIButton loadGameButton;
public Action<SubmarineInfo, string, string, CampaignSettings> StartNewGame; public Action<SubmarineInfo, string, string, CampaignSettings> StartNewGame;

View File

@@ -7,6 +7,7 @@ using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using System.Globalization; using System.Globalization;
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Barotrauma.Networking;
namespace Barotrauma namespace Barotrauma
{ {
@@ -14,55 +15,79 @@ namespace Barotrauma
{ {
private GUIButton deleteMpSaveButton; private GUIButton deleteMpSaveButton;
public MultiPlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<SubmarineInfo> submarines, IEnumerable<string> saveFiles = null) public MultiPlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<string> saveFiles = null)
: base(newGameContainer, loadGameContainer) : base(newGameContainer, loadGameContainer)
{ {
var columnContainer = new GUILayoutGroup(new RectTransform(Vector2.One, newGameContainer.RectTransform), isHorizontal: true) var verticalLayout = new GUILayoutGroup(new RectTransform(Vector2.One, newGameContainer.RectTransform), isHorizontal: false)
{ {
Stretch = true, Stretch = true,
RelativeSpacing = 0.0f RelativeSpacing = 0.0f
}; };
var leftColumn = new GUILayoutGroup(new RectTransform(Vector2.One, columnContainer.RectTransform)) // New game
{ new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), verticalLayout.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("SaveName"), font: GUI.SubHeadingFont, textAlignment: Alignment.BottomLeft);
Stretch = true, saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), verticalLayout.RectTransform) { MinSize = new Point(0, 20) }, string.Empty)
RelativeSpacing = 0.015f
};
var rightColumn = new GUILayoutGroup(new RectTransform(Vector2.Zero, columnContainer.RectTransform))
{
Stretch = true,
RelativeSpacing = 0.015f
};
columnContainer.Recalculate();
// New game left side
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("SaveName"), font: GUI.SubHeadingFont);
saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, string.Empty)
{ {
textFilterFunction = (string str) => { return ToolBox.RemoveInvalidFileNameChars(str); } textFilterFunction = (string str) => { return ToolBox.RemoveInvalidFileNameChars(str); }
}; };
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("MapSeed"), font: GUI.SubHeadingFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), verticalLayout.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("MapSeed"), font: GUI.SubHeadingFont, textAlignment: Alignment.BottomLeft);
seedBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, ToolBox.RandomSeed(8)); seedBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), verticalLayout.RectTransform) { MinSize = new Point(0, 20) }, ToolBox.RandomSeed(8));
// Spacing to fix the multiplayer campaign setup layout GUIFrame radiationBoxContainer
CreateMultiplayerCampaignSubList(leftColumn.RectTransform); = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), verticalLayout.RectTransform), style: null);
GUITickBox radiationEnabledTickBox = null;
//spacing if (MapGenerationParams.Instance.RadiationParams != null)
//new GUIFrame(new RectTransform(new Vector2(1.0f, 0.25f), leftColumn.RectTransform), style: null);
// New game right side
subPreviewContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), rightColumn.RectTransform))
{ {
Stretch = true radiationEnabledTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.5f), radiationBoxContainer.RectTransform, Anchor.Center), TextManager.Get("CampaignOption.EnableRadiation"), font: GUI.Style.Font)
{
Selected = true,
OnSelected = box => true
};
}
var maxMissionCountSettingHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), verticalLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
var maxMissionCountDescription = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.0f), maxMissionCountSettingHolder.RectTransform), TextManager.Get("maxmissioncount", fallBackTag: "missions"), wrap: true)
{
ToolTip = TextManager.Get("maxmissioncounttooltip")
}; };
int maxMissionCount = GameMain.NetworkMember.ServerSettings.MaxMissionCount;
var maxMissionCountContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), maxMissionCountSettingHolder.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { RelativeSpacing = 0.05f, Stretch = true };
var maxMissionCountButtons = new GUIButton[2];
maxMissionCountButtons[0]
= new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), maxMissionCountContainer.RectTransform),
style: "GUIButtonToggleLeft");
var maxMissionCountText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), maxMissionCountContainer.RectTransform), "0", textAlignment: Alignment.Center, style: "GUITextBox");
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.12f), void updateMissionCountText()
leftColumn.RectTransform) { MaxSize = new Point(int.MaxValue, 60) }, childAnchor: Anchor.BottomRight, isHorizontal: true); {
maxMissionCount = MathHelper.Clamp(maxMissionCount,
CampaignSettings.MinMissionCountLimit,
CampaignSettings.MaxMissionCountLimit);
maxMissionCountText.Text = maxMissionCount.ToString(CultureInfo.InvariantCulture);
}
maxMissionCountButtons[1]
= new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), maxMissionCountContainer.RectTransform),
style: "GUIButtonToggleRight");
maxMissionCountButtons[0].OnClicked = (button, o) =>
{
maxMissionCount--;
updateMissionCountText();
return false;
};
maxMissionCountButtons[1].OnClicked = (button, o) =>
{
maxMissionCount++;
updateMissionCountText();
return false;
};
updateMissionCountText();
maxMissionCountSettingHolder.Children.ForEach(c => c.ToolTip = maxMissionCountSettingHolder.ToolTip);
StartButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1f), buttonContainer.RectTransform, Anchor.BottomRight) { MaxSize = new Point(350, 60) }, TextManager.Get("StartCampaignButton")) var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.04f),
verticalLayout.RectTransform) { MaxSize = new Point(int.MaxValue, 60) }, childAnchor: Anchor.BottomRight, isHorizontal: true);
StartButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1f), buttonContainer.RectTransform, Anchor.BottomRight), TextManager.Get("StartCampaignButton"))
{ {
OnClicked = (GUIButton btn, object userData) => OnClicked = (GUIButton btn, object userData) =>
{ {
@@ -85,20 +110,19 @@ namespace Barotrauma
if (string.IsNullOrEmpty(selectedSub.MD5Hash.Hash)) if (string.IsNullOrEmpty(selectedSub.MD5Hash.Hash))
{ {
((GUITextBlock)subList.SelectedComponent).TextColor = Color.DarkRed * 0.8f; new GUIMessageBox(TextManager.Get("error"), TextManager.Get("nohashsubmarineselected"));
subList.SelectedComponent.CanBeFocused = false;
subList.Deselect();
return false; return false;
} }
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveNameBox.Text); string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveNameBox.Text);
bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled; bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled;
CampaignSettings settings = new CampaignSettings(); CampaignSettings settings = new CampaignSettings
{
RadiationEnabled = radiationEnabledTickBox?.Selected ?? GameMain.NetworkMember.ServerSettings.RadiationEnabled,
MaxMissionCount = maxMissionCount
};
settings.RadiationEnabled = GameMain.NetLobbyScreen.IsRadiationEnabled();
settings.MaxMissionCount = GameMain.NetLobbyScreen.GetMaxMissionCount();
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages) if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages)
{ {
if (!hasRequiredContentPackages) if (!hasRequiredContentPackages)
@@ -148,7 +172,9 @@ namespace Barotrauma
return true; return true;
} }
}; };
StartButton.RectTransform.MaxSize = RectTransform.MaxPoint;
StartButton.Children.ForEach(c => c.RectTransform.MaxSize = RectTransform.MaxPoint);
InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), buttonContainer.RectTransform), "", font: GUI.Style.SmallFont, textColor: GUI.Style.Green) InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), buttonContainer.RectTransform), "", font: GUI.Style.SmallFont, textColor: GUI.Style.Green)
{ {
TextGetter = () => TextGetter = () =>
@@ -163,114 +189,11 @@ namespace Barotrauma
} }
}; };
columnContainer.Recalculate(); verticalLayout.Recalculate();
leftColumn.Recalculate();
rightColumn.Recalculate();
if (submarines != null) { UpdateSubList(submarines); }
UpdateLoadMenu(saveFiles); UpdateLoadMenu(saveFiles);
} }
private void CreateMultiplayerCampaignSubList(RectTransform parent)
{
GUILayoutGroup subHolder = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.725f), parent))
{
RelativeSpacing = 0.005f,
Stretch = true
};
var subLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.055f), subHolder.RectTransform) { MinSize = new Point(0, 25) }, TextManager.Get("purchasablesubmarines", fallBackTag: "workshoplabelsubmarines"), font: GUI.SubHeadingFont);
var filterContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subHolder.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 subList.Content.Children)
{
if (!(child.UserData is SubmarineInfo sub)) { continue; }
child.Visible = string.IsNullOrEmpty(text) ? true : sub.DisplayName.ToLower().Contains(text.ToLower());
}
return true;
};
subList = new GUIListBox(new RectTransform(Vector2.One, subHolder.RectTransform));
subTickBoxes = new List<GUITickBox>();
for (int i = 0; i < GameMain.Client.ServerSubmarines.Count; i++)
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[i];
if (!sub.IsCampaignCompatible) continue;
var frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), subList.Content.RectTransform) { MinSize = new Point(0, 20) },
style: "ListBoxElement")
{
ToolTip = sub.Description,
UserData = sub
};
int buttonSize = (int)(frame.Rect.Height * 0.8f);
GUITickBox tickBox = new GUITickBox(new RectTransform(new Vector2(0.8f, 1.0f), frame.RectTransform, Anchor.CenterLeft), ToolBox.LimitString(sub.DisplayName, GUI.Font, subList.Content.Rect.Width - 65))
{
UserData = sub,
OnSelected = (GUITickBox box) =>
{
GameMain.Client.RequestCampaignSub(box.UserData as SubmarineInfo, box.Selected);
return true;
}
};
subTickBoxes.Add(tickBox);
tickBox.Selected = GameMain.NetLobbyScreen.CampaignSubmarines.Contains(sub);
frame.RectTransform.MinSize = new Point(0, tickBox.RectTransform.MinSize.Y);
var subTextBlock = tickBox.TextBlock;
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name && s.MD5Hash?.Hash == sub.MD5Hash?.Hash);
if (matchingSub == null) matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name);
if (matchingSub == null)
{
subTextBlock.TextColor = new Color(subTextBlock.TextColor, 0.5f);
frame.ToolTip = TextManager.Get("SubNotFound");
}
else if (matchingSub?.MD5Hash == null || matchingSub.MD5Hash?.Hash != sub.MD5Hash?.Hash)
{
subTextBlock.TextColor = new Color(subTextBlock.TextColor, 0.5f);
frame.ToolTip = TextManager.Get("SubDoesntMatch");
}
if (!sub.RequiredContentPackagesInstalled)
{
subTextBlock.TextColor = Color.Lerp(subTextBlock.TextColor, Color.DarkRed, 0.5f);
frame.ToolTip = TextManager.Get("ContentPackageMismatch") + "\n\n" + frame.RawToolTip;
}
var classText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), frame.RectTransform, Anchor.CenterRight),
TextManager.Get($"submarineclass.{sub.SubmarineClass}"), textAlignment: Alignment.CenterRight, font: GUI.SmallFont)
{
TextColor = subTextBlock.TextColor * 0.8f,
ToolTip = subTextBlock.RawToolTip
};
}
}
public void RefreshMultiplayerCampaignSubUI(List<SubmarineInfo> campaignSubs)
{
for (int i = 0; i < subTickBoxes.Count; i++)
{
subTickBoxes[i].Selected = campaignSubs.Contains(subTickBoxes[i].UserData as SubmarineInfo);
}
}
private IEnumerable<CoroutineStatus> WaitForCampaignSetup() private IEnumerable<CoroutineStatus> WaitForCampaignSetup()
{ {
GUI.SetCursorWaiting(); GUI.SetCursorWaiting();
@@ -298,64 +221,6 @@ namespace Barotrauma
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
public void UpdateSubList(IEnumerable<SubmarineInfo> submarines)
{
List<SubmarineInfo> subsToShow;
string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
subsToShow = submarines.Where(s => s.IsCampaignCompatibleIgnoreClass && Path.GetDirectoryName(Path.GetFullPath(s.FilePath)) != downloadFolder).ToList();
subsToShow.Sort((s1, s2) =>
{
int p1 = s1.Price > CampaignMode.InitialMoney ? 10 : 0;
int p2 = s2.Price > CampaignMode.InitialMoney ? 10 : 0;
return p1.CompareTo(p2) * 100 + s1.Name.CompareTo(s2.Name);
});
subList.ClearChildren();
foreach (SubmarineInfo sub in subsToShow)
{
var textBlock = new GUITextBlock(
new RectTransform(new Vector2(1, 0.1f), subList.Content.RectTransform) { MinSize = new Point(0, 30) },
ToolBox.LimitString(sub.DisplayName, GUI.Font, subList.Rect.Width - 65), style: "ListBoxElement")
{
ToolTip = sub.Description,
UserData = sub
};
if (!sub.RequiredContentPackagesInstalled)
{
textBlock.TextColor = Color.Lerp(textBlock.TextColor, Color.DarkRed, .5f);
textBlock.ToolTip = TextManager.Get("ContentPackageMismatch") + "\n\n" + textBlock.RawToolTip;
}
var priceText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), textBlock.RectTransform, Anchor.CenterRight),
TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", sub.Price)), textAlignment: Alignment.CenterRight, font: GUI.SmallFont)
{
TextColor = sub.Price > CampaignMode.InitialMoney ? GUI.Style.Red : textBlock.TextColor * 0.8f,
ToolTip = textBlock.ToolTip
};
#if !DEBUG
if (!GameMain.DebugDraw)
{
if (sub.Price > CampaignMode.InitialMoney || !sub.IsCampaignCompatible)
{
textBlock.CanBeFocused = false;
textBlock.TextColor *= 0.5f;
}
}
#endif
}
if (SubmarineInfo.SavedSubmarines.Any())
{
var validSubs = subsToShow.Where(s => s.IsCampaignCompatible && s.Price <= CampaignMode.InitialMoney).ToList();
if (validSubs.Count > 0)
{
subList.Select(validSubs[Rand.Int(validSubs.Count)]);
}
}
}
private List<string> prevSaveFiles; private List<string> prevSaveFiles;
public void UpdateLoadMenu(IEnumerable<string> saveFiles = null) public void UpdateLoadMenu(IEnumerable<string> saveFiles = null)
{ {

View File

@@ -12,6 +12,10 @@ namespace Barotrauma
{ {
class SinglePlayerCampaignSetupUI : CampaignSetupUI class SinglePlayerCampaignSetupUI : CampaignSetupUI
{ {
private GUIListBox subList;
protected GUILayoutGroup subPreviewContainer;
public CharacterInfo.AppearanceCustomizationMenu[] CharacterMenus { get; private set; } public CharacterInfo.AppearanceCustomizationMenu[] CharacterMenus { get; private set; }
private GUIButton nextButton; private GUIButton nextButton;
@@ -38,7 +42,7 @@ namespace Barotrauma
pageContainer.BarScroll = targetScroll; pageContainer.BarScroll = targetScroll;
} }
for (int i=0; i<CharacterMenus.Length; i++) for (int i = 0; i < CharacterMenus.Length; i++)
{ {
CharacterMenus[i]?.Update(); CharacterMenus[i]?.Update();
} }
@@ -77,6 +81,7 @@ namespace Barotrauma
{ {
ScrollBarEnabled = false, ScrollBarEnabled = false,
ScrollBarVisible = false, ScrollBarVisible = false,
AllowArrowKeyScroll = false,
HoverCursor = CursorState.Default HoverCursor = CursorState.Default
}; };

View File

@@ -705,7 +705,7 @@ namespace Barotrauma.CharacterEditor
{ {
string errorMsg = "Attempted to modify the state of the physics simulation while a time step was running."; string errorMsg = "Attempted to modify the state of the physics simulation while a time step was running.";
DebugConsole.ThrowError(errorMsg, e); DebugConsole.ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("CharacterEditorScreen.Update:WorldLockedException" + e.Message, GameAnalyticsSDK.Net.EGAErrorSeverity.Critical, errorMsg); GameAnalyticsManager.AddErrorEventOnce("CharacterEditorScreen.Update:WorldLockedException" + e.Message, GameAnalyticsManager.ErrorSeverity.Critical, errorMsg);
} }
} }
// Camera // Camera

View File

@@ -732,9 +732,7 @@ namespace Barotrauma
if (!string.IsNullOrEmpty(subName)) if (!string.IsNullOrEmpty(subName))
{ {
DebugConsole.NewMessage($"Loading the predefined quick start sub \"{subName}\"", Color.White); DebugConsole.NewMessage($"Loading the predefined quick start sub \"{subName}\"", Color.White);
selectedSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => selectedSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name.ToLowerInvariant() == subName.ToLowerInvariant());
s.Name.ToLower() == subName.ToLower());
if (selectedSub == null) if (selectedSub == null)
{ {
DebugConsole.NewMessage($"Cannot find a sub that matches the name \"{subName}\".", Color.Red); DebugConsole.NewMessage($"Cannot find a sub that matches the name \"{subName}\".", Color.Red);
@@ -1055,17 +1053,23 @@ namespace Barotrauma
GUI.Draw(Cam, spriteBatch); GUI.Draw(Cam, spriteBatch);
#if !UNSTABLE
string versionString = "Barotrauma v" + GameMain.Version + " (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")";
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
if (selectedTab != Tab.Credits) if (selectedTab != Tab.Credits)
{ {
#if !UNSTABLE
string versionString = "Barotrauma v" + GameMain.Version + " (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")";
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
string gameAnalyticsStatus = TextManager.Get($"GameAnalyticsStatus.{GameAnalyticsManager.UserConsented}");
Vector2 textSize = GUI.SmallFont.MeasureString(gameAnalyticsStatus).ToPoint().ToVector2();
GUI.SmallFont.DrawString(spriteBatch, gameAnalyticsStatus, new Vector2(HUDLayoutSettings.Padding, GameMain.GraphicsHeight - GUI.SmallFont.LineHeight * 2 - HUDLayoutSettings.Padding * 0.75f), Color.White * 0.7f);
Vector2 textPos = new Vector2(GameMain.GraphicsWidth - HUDLayoutSettings.Padding, GameMain.GraphicsHeight - HUDLayoutSettings.Padding * 0.75f); 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]); textSize = GUI.SmallFont.MeasureString(legalCrap[i])
textSize = new Vector2((int)textSize.X, (int)textSize.Y); .ToPoint().ToVector2();
bool mouseOn = i == 0 && bool mouseOn = i == 0 &&
PlayerInput.MousePosition.X > textPos.X - textSize.X && PlayerInput.MousePosition.X < textPos.X && PlayerInput.MousePosition.X > textPos.X - textSize.X && PlayerInput.MousePosition.X < textPos.X &&
PlayerInput.MousePosition.Y > textPos.Y - textSize.Y && PlayerInput.MousePosition.Y < textPos.Y; PlayerInput.MousePosition.Y > textPos.Y - textSize.Y && PlayerInput.MousePosition.Y < textPos.Y;
@@ -1121,8 +1125,8 @@ namespace Barotrauma
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(
"MainMenuScreen.StartGame:IOException" + selectedSub.Name, "MainMenuScreen.StartGame:IOException" + selectedSub.Name,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
"Copying the file \"" + selectedSub.FilePath + "\" failed.\n" + e.Message + "\n" + Environment.StackTrace.CleanupStackTrace()); "Copying a submarine file failed. " + e.Message + "\n" + Environment.StackTrace.CleanupStackTrace());
return; return;
} }
@@ -1446,7 +1450,7 @@ namespace Barotrauma
#if DEBUG #if DEBUG
DebugConsole.ThrowError("Fetching remote content to the main menu failed.", e); DebugConsole.ThrowError("Fetching remote content to the main menu failed.", e);
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.FetchRemoteContent:Exception", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.FetchRemoteContent:Exception", GameAnalyticsManager.ErrorSeverity.Error,
"Fetching remote content to the main menu failed. " + e.Message); "Fetching remote content to the main menu failed. " + e.Message);
return; return;
} }
@@ -1489,7 +1493,7 @@ namespace Barotrauma
#if DEBUG #if DEBUG
DebugConsole.ThrowError("Reading received remote main menu content failed.", e); DebugConsole.ThrowError("Reading received remote main menu content failed.", e);
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.WairForRemoteContentReceived:Exception", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.WairForRemoteContentReceived:Exception", GameAnalyticsManager.ErrorSeverity.Error,
"Reading received remote main menu content failed. " + e.Message); "Reading received remote main menu content failed. " + e.Message);
} }
} }

View File

@@ -7,6 +7,7 @@ using System.Collections.Generic;
using Barotrauma.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Steam;
namespace Barotrauma namespace Barotrauma
{ {
@@ -40,12 +41,6 @@ namespace Barotrauma
private readonly GUIScrollBar levelDifficultyScrollBar; private readonly GUIScrollBar levelDifficultyScrollBar;
private readonly GUITickBox radiationEnabledTickBox;
private readonly GUIButton[] maxMissionCountButtons;
private readonly GUITextBlock maxMissionCountText;
private readonly GUITextBlock maxMissionCountDescription;
private readonly GUIButton[] traitorProbabilityButtons; private readonly GUIButton[] traitorProbabilityButtons;
private readonly GUITextBlock traitorProbabilityText; private readonly GUITextBlock traitorProbabilityText;
@@ -192,6 +187,9 @@ namespace Barotrauma
} }
} }
public IReadOnlyList<SubmarineInfo> GetSubList()
=> SubList.Content.Children.Select(c => c.UserData as SubmarineInfo).ToArray();
public readonly GUIListBox PlayerList; public readonly GUIListBox PlayerList;
public GUITextBox CharacterNameBox public GUITextBox CharacterNameBox
@@ -1185,45 +1183,6 @@ namespace Barotrauma
} }
}; };
if (MapGenerationParams.Instance.RadiationParams != null)
{
radiationEnabledTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.1f), settingsContent.RectTransform), TextManager.Get("CampaignOption.EnableRadiation"), font: GUI.Style.Font)
{
Selected = true,
OnSelected = box =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, radiationEnabled: box.Selected);
return true;
}
};
}
var maxMissionCountSettingHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), settingsContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
maxMissionCountDescription = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.0f), maxMissionCountSettingHolder.RectTransform), TextManager.Get("maxmissioncount", fallBackTag: "missions"), wrap: true)
{
ToolTip = TextManager.Get("maxmissioncounttooltip")
};
var maxMissionCountContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), maxMissionCountSettingHolder.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { RelativeSpacing = 0.05f, Stretch = true };
maxMissionCountButtons = new GUIButton[2];
maxMissionCountButtons[0] = new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), maxMissionCountContainer.RectTransform), style: "GUIButtonToggleLeft")
{
OnClicked = (button, obj) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, maxMissionCount: -1);
return true;
}
};
maxMissionCountText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), maxMissionCountContainer.RectTransform), "0", textAlignment: Alignment.Center, style: "GUITextBox");
maxMissionCountButtons[1] = new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), maxMissionCountContainer.RectTransform), style: "GUIButtonToggleRight")
{
OnClicked = (button, obj) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, maxMissionCount: 1);
return true;
}
};
maxMissionCountSettingHolder.Children.ForEach(c => c.ToolTip = maxMissionCountSettingHolder.ToolTip);
List<GUIComponent> settingsElements = settingsContent.Children.ToList(); List<GUIComponent> settingsElements = settingsContent.Children.ToList();
for (int i = 0; i < settingsElements.Count; i++) for (int i = 0; i < settingsElements.Count; i++)
{ {
@@ -1372,16 +1331,6 @@ namespace Barotrauma
} }
SeedBox.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings); SeedBox.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
levelDifficultyScrollBar.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings); levelDifficultyScrollBar.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
if (radiationEnabledTickBox != null)
{
radiationEnabledTickBox.Enabled = CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
}
maxMissionCountDescription.Enabled = CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
maxMissionCountText.Enabled = CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
foreach (var button in maxMissionCountButtons)
{
button.Enabled = CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
}
traitorProbabilityButtons[0].Enabled = traitorProbabilityButtons[1].Enabled = traitorProbabilityText.Enabled = traitorProbabilityButtons[0].Enabled = traitorProbabilityButtons[1].Enabled = traitorProbabilityText.Enabled =
!CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings); !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
@@ -2163,17 +2112,126 @@ namespace Barotrauma
if (child != null) { PlayerList.RemoveChild(child); } if (child != null) { PlayerList.RemoveChild(child); }
} }
private Client ExtractClientFromClickableArea(GUITextBlock.ClickableArea area)
{
if (!UInt64.TryParse(area.Data.Metadata, out UInt64 id)) { return null; }
Client client = GameMain.Client.ConnectedClients.Find(c => c.SteamID == id)
?? GameMain.Client.ConnectedClients.Find(c => c.ID == id)
?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.SteamID == id)
?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.ID == id);
return client;
}
public void SelectPlayer(GUITextBlock component, GUITextBlock.ClickableArea area) public void SelectPlayer(GUITextBlock component, GUITextBlock.ClickableArea area)
{ {
if (!UInt64.TryParse(area.Data.Metadata, out UInt64 id)) { return; } var client = ExtractClientFromClickableArea(area);
Client client = GameMain.Client.ConnectedClients.Find(c => c.SteamID == id) if (client is null) { return; }
?? GameMain.Client.ConnectedClients.Find(c => c.ID == id)
?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.SteamID == id)
?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.ID == id);
if (client == null) { return; }
GameMain.NetLobbyScreen.SelectPlayer(client); GameMain.NetLobbyScreen.SelectPlayer(client);
} }
public void ShowPlayerContextMenu(GUITextBlock component, GUITextBlock.ClickableArea area)
{
var client = ExtractClientFromClickableArea(area);
if (client is null) { return; }
CreateModerationContextMenu(client);
}
#region Context Menu
public static void CreateModerationContextMenu(Client client)
{
if (GUIContextMenu.CurrentContextMenu != null) { return; }
if (GameMain.IsSingleplayer || client == null || ((!GameMain.Client?.PreviouslyConnectedClients?.Contains(client)) ?? true)) { return; }
bool hasSteam = client.SteamID > 0 && SteamManager.IsInitialized,
canKick = GameMain.Client.HasPermission(ClientPermissions.Kick),
canBan = GameMain.Client.HasPermission(ClientPermissions.Ban) && client.AllowKicking,
canPromo = GameMain.Client.HasPermission(ClientPermissions.ManagePermissions);
// Disable options if we are targeting ourselves
if (client.ID == GameMain.Client?.ID)
{
canKick = canBan = canPromo = false;
}
List<ContextMenuOption> options = new List<ContextMenuOption>();
options.Add(new ContextMenuOption("ViewSteamProfile", isEnabled: hasSteam, onSelected: delegate
{
Steamworks.SteamFriends.OpenWebOverlay($"https://steamcommunity.com/profiles/{client.SteamID}");
}));
options.Add(new ContextMenuOption("ModerationMenu.UserDetails", isEnabled: true, onSelected: delegate
{
GameMain.NetLobbyScreen?.SelectPlayer(client);
}));
// Creates sub context menu options for all the ranks
List<ContextMenuOption> permissionOptions = new List<ContextMenuOption>();
foreach (PermissionPreset rank in PermissionPreset.List)
{
permissionOptions.Add(new ContextMenuOption(rank.Name, isEnabled: true, onSelected: () =>
{
string label = TextManager.GetWithVariables(rank.Permissions == ClientPermissions.None ? "clearrankprompt" : "giverankprompt", new []{ "[user]", "[rank]" }, new []{ client.Name, rank.Name });
GUIMessageBox msgBox = new GUIMessageBox(string.Empty, label, new[] { TextManager.Get("Yes"), TextManager.Get("Cancel") });
msgBox.Buttons[0].OnClicked = delegate
{
client.SetPermissions(rank.Permissions, rank.PermittedCommands);
GameMain.Client.UpdateClientPermissions(client);
msgBox.Close();
return true;
};
msgBox.Buttons[1].OnClicked = delegate
{
msgBox.Close();
return true;
};
}) { Tooltip = rank.Description });
}
options.Add(new ContextMenuOption("Permissions", isEnabled: canPromo, options: permissionOptions.ToArray()));
Color clientColor = client.Character?.Info?.Job.Prefab.UIColor ?? Color.White;
if (GameMain.Client.ConnectedClients.Contains(client))
{
options.Add(new ContextMenuOption(client.MutedLocally ? "Unmute" : "Mute", isEnabled: client.ID != GameMain.Client?.ID, onSelected: delegate
{
client.MutedLocally = !client.MutedLocally;
}));
bool kickEnabled = client.ID != GameMain.Client?.ID && client.AllowKicking;
// if the user can kick create a kick option else create the votekick option
ContextMenuOption kickOption;
if (canKick)
{
kickOption = new ContextMenuOption("Kick", isEnabled: kickEnabled, onSelected: delegate
{
GameMain.Client?.CreateKickReasonPrompt(client.Name, false);
});
}
else
{
kickOption = new ContextMenuOption("VoteToKick", isEnabled: kickEnabled, onSelected: delegate
{
GameMain.Client?.VoteForKick(client);
});
}
options.Add(kickOption);
}
options.Add(new ContextMenuOption("Ban", isEnabled: canBan, onSelected: delegate
{
GameMain.Client?.CreateKickReasonPrompt(client.Name, true);
}));
GUIContextMenu.CreateContextMenu(null, client.Name, headerColor: clientColor, options.ToArray());
}
#endregion
public bool SelectPlayer(Client selectedClient) public bool SelectPlayer(Client selectedClient)
{ {
bool myClient = selectedClient.ID == GameMain.Client.ID; bool myClient = selectedClient.ID == GameMain.Client.ID;
@@ -2763,7 +2821,8 @@ namespace Barotrauma
msg.ClickableAreas.Add(new GUITextBlock.ClickableArea() msg.ClickableAreas.Add(new GUITextBlock.ClickableArea()
{ {
Data = data, Data = data,
OnClick = GameMain.NetLobbyScreen.SelectPlayer OnClick = GameMain.NetLobbyScreen.SelectPlayer,
OnSecondaryClick = GameMain.NetLobbyScreen.ShowPlayerContextMenu
}); });
} }
} }
@@ -3221,6 +3280,7 @@ namespace Barotrauma
{ {
CreateSubPreview(submarine); CreateSubPreview(submarine);
} }
UpdateSubVisibility();
} }
private bool ViewJobInfo(GUIButton button, object obj) private bool ViewJobInfo(GUIButton button, object obj)
@@ -3369,14 +3429,28 @@ namespace Barotrauma
return btn; return btn;
} }
public Pair<string, string> FailedSelectedSub; public readonly struct FailedSubInfo
public Pair<string, string> FailedSelectedShuttle; {
public readonly string Name;
public readonly string Hash;
public FailedSubInfo(string name, string hash) { Name = name; Hash = hash; }
public void Deconstruct(out string name, out string hash) { name = Name; hash = Hash; }
public static bool operator ==(FailedSubInfo a, FailedSubInfo b)
=> a.Name.Equals(b.Name, StringComparison.OrdinalIgnoreCase)
&& a.Hash.Equals(b.Hash, StringComparison.OrdinalIgnoreCase);
public static bool operator !=(FailedSubInfo a, FailedSubInfo b)
=> !(a == b);
}
public List<Pair<string, string>> FailedCampaignSubs = new List<Pair<string, string>>(); public FailedSubInfo? FailedSelectedSub;
public List<Pair<string, string>> FailedOwnedSubs = new List<Pair<string, string>>(); public FailedSubInfo? FailedSelectedShuttle;
public List<FailedSubInfo> FailedCampaignSubs = new List<FailedSubInfo>();
public List<FailedSubInfo> FailedOwnedSubs = new List<FailedSubInfo>();
public bool TrySelectSub(string subName, string md5Hash, GUIListBox subList) public bool TrySelectSub(string subName, string md5Hash, GUIListBox subList)
{ {
UpdateSubVisibility();
if (GameMain.Client == null) { return false; } if (GameMain.Client == null) { return false; }
//already downloading the selected sub file //already downloading the selected sub file
@@ -3441,9 +3515,13 @@ namespace Barotrauma
//if we get to this point, a matching sub was not found or it has an incorrect MD5 hash //if we get to this point, a matching sub was not found or it has an incorrect MD5 hash
if (subList == SubList) if (subList == SubList)
FailedSelectedSub = new Pair<string, string>(subName, md5Hash); {
FailedSelectedSub = new FailedSubInfo(subName, md5Hash);
}
else else
FailedSelectedShuttle = new Pair<string, string>(subName, md5Hash); {
FailedSelectedShuttle = new FailedSubInfo(subName, md5Hash);
}
string errorMsg = ""; string errorMsg = "";
if (sub == null || !SubmarineInfo.SavedSubmarines.Contains(sub)) if (sub == null || !SubmarineInfo.SavedSubmarines.Contains(sub))
@@ -3542,22 +3620,22 @@ namespace Barotrauma
{ {
UserData = "request" + serverSubmarine.Name UserData = "request" + serverSubmarine.Name
}; };
requestFileBox.Buttons[0].UserData = new string[] { serverSubmarine.Name, serverSubmarine.MD5Hash.Hash }; requestFileBox.Buttons[0].UserData = new FailedSubInfo(serverSubmarine.Name, serverSubmarine.MD5Hash.Hash);
requestFileBox.Buttons[0].OnClicked += requestFileBox.Close; requestFileBox.Buttons[0].OnClicked += requestFileBox.Close;
requestFileBox.Buttons[0].OnClicked += (GUIButton button, object userdata) => requestFileBox.Buttons[0].OnClicked += (GUIButton button, object userdata) =>
{ {
string[] fileInfo = (string[])userdata; FailedSubInfo fileInfo = (FailedSubInfo)userdata;
if (deliveryData == "owned") if (deliveryData == "owned") //owned!!!!
{ {
FailedOwnedSubs.Add(new Pair<string, string>(fileInfo[0], fileInfo[1])); FailedOwnedSubs.Add(fileInfo);
} }
else if (deliveryData == "campaign") else if (deliveryData == "campaign")
{ {
FailedCampaignSubs.Add(new Pair<string, string>(fileInfo[0], fileInfo[1])); FailedCampaignSubs.Add(fileInfo);
} }
GameMain.Client?.RequestFile(FileTransferType.Submarine, fileInfo[0], fileInfo[1]); GameMain.Client?.RequestFile(FileTransferType.Submarine, fileInfo.Name, fileInfo.Hash);
return true; return true;
}; };
requestFileBox.Buttons[1].OnClicked += requestFileBox.Close; requestFileBox.Buttons[1].OnClicked += requestFileBox.Close;
@@ -3691,7 +3769,7 @@ namespace Barotrauma
}; };
var subName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), frameContent.RectTransform), var subName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), frameContent.RectTransform),
text: sub.Name) text: sub.DisplayName)
{ {
CanBeFocused = false CanBeFocused = false
}; };
@@ -3823,7 +3901,9 @@ namespace Barotrauma
foreach (GUIComponent child in SubList.Content.Children) foreach (GUIComponent child in SubList.Content.Children)
{ {
if (!(child.UserData is SubmarineInfo sub)) { continue; } if (!(child.UserData is SubmarineInfo sub)) { continue; }
child.Visible = !GameMain.Client.ServerSettings.HiddenSubs.Contains(sub.Name) child.Visible =
(!GameMain.Client.ServerSettings.HiddenSubs.Contains(sub.Name)
|| (GameMain.GameSession?.SubmarineInfo != null && GameMain.GameSession.SubmarineInfo.Name.Equals(sub.Name, StringComparison.OrdinalIgnoreCase)))
&& (string.IsNullOrEmpty(subSearchBox.Text) || sub.DisplayName.Contains(subSearchBox.Text, StringComparison.OrdinalIgnoreCase)); && (string.IsNullOrEmpty(subSearchBox.Text) || sub.DisplayName.Contains(subSearchBox.Text, StringComparison.OrdinalIgnoreCase));
} }
} }

View File

@@ -2376,10 +2376,9 @@ namespace Barotrauma
} }
catch (Exception ex) catch (Exception ex)
{ {
string errorMsg = "Failed to ping a server (" + ip + ") - " + (ex?.InnerException?.Message ?? ex.Message); GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + ip, GameAnalyticsManager.ErrorSeverity.Warning, "Failed to ping a server - " + (ex?.InnerException?.Message ?? ex.Message));
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + ip, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, errorMsg);
#if DEBUG #if DEBUG
DebugConsole.NewMessage(errorMsg, Color.Red); DebugConsole.NewMessage("Failed to ping a server (" + ip + ") - " + (ex?.InnerException?.Message ?? ex.Message), Color.Red);
#endif #endif
} }
} }

View File

@@ -824,9 +824,8 @@ namespace Barotrauma
} }
catch (Exception e) catch (Exception e)
{ {
string errorMsg = "Failed to save workshop item preview image to \"" + previewImagePath + "\".";
GameAnalyticsManager.AddErrorEventOnce("SteamWorkshopScreen.OnItemPreviewDownloaded:WriteAllBytesFailed" + previewImagePath, GameAnalyticsManager.AddErrorEventOnce("SteamWorkshopScreen.OnItemPreviewDownloaded:WriteAllBytesFailed" + previewImagePath,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + e.Message); GameAnalyticsManager.ErrorSeverity.Error, "Failed to save workshop item preview image.\n" + e.Message);
return; return;
} }
} }
@@ -1195,7 +1194,7 @@ namespace Barotrauma
{ {
string errorMsg = "Failed to edit workshop item (content package null)\n" + Environment.StackTrace.CleanupStackTrace(); string errorMsg = "Failed to edit workshop item (content package null)\n" + Environment.StackTrace.CleanupStackTrace();
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("SteamWorkshopScreen.ShowCreateItemFrame:ContentPackageNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("SteamWorkshopScreen.ShowCreateItemFrame:ContentPackageNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }

View File

@@ -358,7 +358,7 @@ namespace Barotrauma
} }
else else
{ {
displayName = TextManager.Get(fallbackTag, true); displayName = TextManager.Get(fallbackTag, true) ?? TextManager.Get($"sp.{fallbackTag}.name", true);
} }
} }

View File

@@ -228,7 +228,7 @@ namespace Barotrauma.Sounds
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex); uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
float effectiveGain = gain; float effectiveGain = gain;
if (category != null) effectiveGain *= Sound.Owner.GetCategoryGainMultiplier(category); if (category != null) { effectiveGain *= Sound.Owner.GetCategoryGainMultiplier(category); }
Al.Sourcef(alSource, Al.Gain, effectiveGain); Al.Sourcef(alSource, Al.Gain, effectiveGain);
int alError = Al.GetError(); int alError = Al.GetError();

View File

@@ -470,14 +470,14 @@ namespace Barotrauma
{ {
string errorMsg = "Failed to update water ambience volume - submarine's movement value invalid (" + movementSoundVolume + ", sub velocity: " + sub.Velocity + ")"; string errorMsg = "Failed to update water ambience volume - submarine's movement value invalid (" + movementSoundVolume + ", sub velocity: " + sub.Velocity + ")";
DebugConsole.Log(errorMsg); DebugConsole.Log(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("SoundPlayer.UpdateWaterAmbience:InvalidVolume", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("SoundPlayer.UpdateWaterAmbience:InvalidVolume", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
movementSoundVolume = 0.0f; movementSoundVolume = 0.0f;
} }
if (!MathUtils.IsValid(insideSubFactor)) if (!MathUtils.IsValid(insideSubFactor))
{ {
string errorMsg = "Failed to update water ambience volume - inside sub value invalid (" + insideSubFactor + ")"; string errorMsg = "Failed to update water ambience volume - inside sub value invalid (" + insideSubFactor + ")";
DebugConsole.Log(errorMsg); DebugConsole.Log(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("SoundPlayer.UpdateWaterAmbience:InvalidVolume", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("SoundPlayer.UpdateWaterAmbience:InvalidVolume", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
insideSubFactor = 0.0f; insideSubFactor = 0.0f;
} }
} }
@@ -609,7 +609,7 @@ namespace Barotrauma
flowSoundChannels[i] = FlowSounds[i].Play(1.0f, FlowSoundRange, soundPos); flowSoundChannels[i] = FlowSounds[i].Play(1.0f, FlowSoundRange, soundPos);
flowSoundChannels[i].Looping = true; flowSoundChannels[i].Looping = true;
} }
flowSoundChannels[i].Gain = Math.Max(flowVolumeRight[i], flowVolumeLeft[i]); flowSoundChannels[i].Gain = Math.Min(Math.Max(flowVolumeRight[i], flowVolumeLeft[i]), 1.0f);
flowSoundChannels[i].Position = new Vector3(soundPos, 0.0f); flowSoundChannels[i].Position = new Vector3(soundPos, 0.0f);
} }
} }
@@ -790,7 +790,7 @@ namespace Barotrauma
if (sound == null) if (sound == null)
{ {
string errorMsg = "Error in SoundPlayer.PlaySound (sound was null)\n" + Environment.StackTrace.CleanupStackTrace(); string errorMsg = "Error in SoundPlayer.PlaySound (sound was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("SoundPlayer.PlaySound:SoundNull" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("SoundPlayer.PlaySound:SoundNull" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return null; return null;
} }

View File

@@ -64,6 +64,7 @@ namespace Barotrauma
{ {
float angle = 0.0f; float angle = 0.0f;
float particleRotation = 0.0f; float particleRotation = 0.0f;
bool mirrorAngle = false;
if (emitter.Prefab.Properties.CopyEntityAngle) if (emitter.Prefab.Properties.CopyEntityAngle)
{ {
Limb targetLimb = null; Limb targetLimb = null;
@@ -71,7 +72,11 @@ namespace Barotrauma
{ {
angle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi); angle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
particleRotation = -item.body.Rotation; particleRotation = -item.body.Rotation;
if (item.body.Dir < 0.0f) { particleRotation += MathHelper.Pi; } if (item.body.Dir < 0.0f)
{
particleRotation += MathHelper.Pi;
mirrorAngle = true;
}
} }
else if (entity is Character c && !c.Removed && targetLimbs?.FirstOrDefault(l => l != LimbType.None) is LimbType l) else if (entity is Character c && !c.Removed && targetLimbs?.FirstOrDefault(l => l != LimbType.None) is LimbType l)
{ {
@@ -85,11 +90,15 @@ namespace Barotrauma
{ {
angle = targetLimb.body.Rotation + ((targetLimb.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi); angle = targetLimb.body.Rotation + ((targetLimb.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
particleRotation = -targetLimb.body.Rotation; particleRotation = -targetLimb.body.Rotation;
if (targetLimb.body.Dir < 0.0f) { particleRotation += MathHelper.Pi; } if (targetLimb.body.Dir < 0.0f)
{
particleRotation += MathHelper.Pi;
mirrorAngle = true;
}
} }
} }
emitter.Emit(deltaTime, worldPosition, hull, angle: angle, particleRotation: particleRotation); emitter.Emit(deltaTime, worldPosition, hull, angle: angle, particleRotation: particleRotation, mirrorAngle: mirrorAngle);
} }
} }
@@ -108,7 +117,7 @@ namespace Barotrauma
if (sound?.Sound == null) if (sound?.Sound == null)
{ {
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace(); string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
soundChannel = SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, hullGuess: hull, ignoreMuffling: sound.IgnoreMuffling); soundChannel = SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, hullGuess: hull, ignoreMuffling: sound.IgnoreMuffling);
@@ -135,7 +144,7 @@ namespace Barotrauma
if (selectedSound?.Sound == null) if (selectedSound?.Sound == null)
{ {
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace(); string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
if (selectedSound.Sound.Disposed) if (selectedSound.Sound.Disposed)

View File

@@ -421,7 +421,7 @@ namespace Barotrauma
#if DEBUG #if DEBUG
DebugConsole.ThrowError("Empty color array passed to the GradientLerp method.\n" + Environment.StackTrace.CleanupStackTrace()); DebugConsole.ThrowError("Empty color array passed to the GradientLerp method.\n" + Environment.StackTrace.CleanupStackTrace());
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("ToolBox.GradientLerp:EmptyColorArray", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.AddErrorEventOnce("ToolBox.GradientLerp:EmptyColorArray", GameAnalyticsManager.ErrorSeverity.Error,
"Empty color array passed to the GradientLerp method.\n" + Environment.StackTrace.CleanupStackTrace()); "Empty color array passed to the GradientLerp method.\n" + Environment.StackTrace.CleanupStackTrace());
return Color.Black; return Color.Black;
} }

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.15.15.0</Version> <Version>0.15.17.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -55,6 +55,7 @@
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Content Remove="..\BarotraumaShared\**\*.props" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
</ItemGroup> </ItemGroup>
@@ -99,7 +100,6 @@
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.Linux.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.Linux.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
@@ -109,7 +109,6 @@
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.Linux.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.Linux.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
@@ -189,4 +188,9 @@
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" /> <WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
</Target> </Target>
<PropertyGroup>
<GARuntime>linux-x64</GARuntime>
</PropertyGroup>
<Import Project="../BarotraumaShared/DeployGameAnalytics.props" />
</Project> </Project>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.15.15.0</Version> <Version>0.15.17.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -58,6 +58,7 @@
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Content Remove="..\BarotraumaShared\**\*.props" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
<Content Remove="..\BarotraumaShared\libsteam_api64.dylib" /> <Content Remove="..\BarotraumaShared\libsteam_api64.dylib" />
<Content Remove="..\BarotraumaShared\libsteam_api64.so" /> <Content Remove="..\BarotraumaShared\libsteam_api64.so" />
@@ -100,7 +101,6 @@
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\SharpFont\Source\SharpFont\SharpFont.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\SharpFont\Source\SharpFont\SharpFont.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
@@ -110,7 +110,6 @@
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\SharpFont\Source\SharpFont\SharpFont.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\SharpFont\Source\SharpFont\SharpFont.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
@@ -201,4 +200,9 @@
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" /> <WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
</Target> </Target>
<PropertyGroup>
<GARuntime>osx-x64</GARuntime>
</PropertyGroup>
<Import Project="../BarotraumaShared/DeployGameAnalytics.props" />
</Project> </Project>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.15.15.0</Version> <Version>0.15.17.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -63,6 +63,7 @@
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Content Remove="..\BarotraumaShared\**\*.props" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
</ItemGroup> </ItemGroup>
@@ -103,7 +104,6 @@
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.Windows.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.Windows.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
@@ -113,7 +113,6 @@
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.Windows.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.Windows.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
@@ -220,4 +219,9 @@
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" /> <WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
</Target> </Target>
<PropertyGroup>
<GARuntime>win-x64</GARuntime>
</PropertyGroup>
<Import Project="../BarotraumaShared/DeployGameAnalytics.props" />
</Project> </Project>

View File

@@ -1,62 +1,62 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/> <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security> <security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" /> <requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges> </requestedPrivileges>
</security> </security>
</trustInfo> </trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application> <application>
<!-- A list of the Windows versions that this application has been tested on <!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. --> and Windows will automatically select the most compatible environment. -->
<!-- Windows Vista --> <!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />--> <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 --> <!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" /> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 --> <!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" /> <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 --> <!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" /> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 --> <!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application> </application>
</compatibility> </compatibility>
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher <!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. --> also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings> <windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
</windowsSettings> </windowsSettings>
</application> </application>
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) --> <!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!-- <!--
<dependency> <dependency>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity <assemblyIdentity
type="win32" type="win32"
name="Microsoft.Windows.Common-Controls" name="Microsoft.Windows.Common-Controls"
version="6.0.0.0" version="6.0.0.0"
processorArchitecture="*" processorArchitecture="*"
publicKeyToken="6595b64144ccf1df" publicKeyToken="6595b64144ccf1df"
language="*" language="*"
/> />
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>
--> -->
</assembly> </assembly>

View File

@@ -1,141 +1,144 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.15.15.0</Version> <Version>0.15.17.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon> <ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
<Configurations>Debug;Release;Unstable</Configurations> <Configurations>Debug;Release;Unstable</Configurations>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE;SERVER;LINUX;USE_STEAM</DefineConstants> <DefineConstants>DEBUG;TRACE;SERVER;LINUX;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DefineConstants>TRACE;DEBUG;SERVER;LINUX;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;DEBUG;SERVER;LINUX;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;SERVER;LINUX;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;LINUX;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'">
<DefineConstants>TRACE;SERVER;LINUX;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;LINUX;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
<Optimize>true</Optimize> <Optimize>true</Optimize>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DefineConstants>TRACE;SERVER;LINUX;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;LINUX;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'">
<DefineConstants>TRACE;SERVER;LINUX;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;LINUX;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
<Optimize>true</Optimize> <Optimize>true</Optimize>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.props" />
<Content Include="DedicatedServer.exe" CopyToOutputDirectory="PreserveNewest" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
</ItemGroup> <Content Include="DedicatedServer.exe" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'!='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" /> <ItemGroup Condition="'$(Configuration)'!='Debug'">
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> </ItemGroup>
</ItemGroup>
<ItemGroup>
<!-- Sourced from https://stackoverflow.com/a/45248069 --> <PackageReference Include="RestSharp" Version="106.13.0" />
<Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''"> </ItemGroup>
<PropertyGroup>
<!-- temp file for the git version (lives in "obj" folder)--> <!-- Sourced from https://stackoverflow.com/a/45248069 -->
<VerFile>$(IntermediateOutputPath)gitver</VerFile> <Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''">
<BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile> <PropertyGroup>
</PropertyGroup> <!-- temp file for the git version (lives in "obj" folder)-->
<VerFile>$(IntermediateOutputPath)gitver</VerFile>
<!-- write the hash to the temp file.--> <BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile>
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true"> </PropertyGroup>
<Output TaskParameter="exitcode" ItemName="exitcodes" />
</Exec> <!-- write the hash to the temp file.-->
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" /> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true">
<Output TaskParameter="exitcode" ItemName="exitcodes" />
<Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" /> </Exec>
<Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" /> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" />
<!-- read the version into the GitVersion itemGroup--> <Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<ReadLinesFromFile File="$(VerFile)"> <Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<Output TaskParameter="Lines" ItemName="GitVersion" />
</ReadLinesFromFile> <!-- read the version into the GitVersion itemGroup-->
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <ReadLinesFromFile File="$(VerFile)">
<PropertyGroup> <Output TaskParameter="Lines" ItemName="GitVersion" />
<BuildHash>@(GitVersion)</BuildHash> </ReadLinesFromFile>
</PropertyGroup> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
<PropertyGroup>
<!-- read the branch into the GitBranch itemGroup--> <BuildHash>@(GitVersion)</BuildHash>
<ReadLinesFromFile File="$(BranchFile)"> </PropertyGroup>
<Output TaskParameter="Lines" ItemName="GitBranch" />
</ReadLinesFromFile> <!-- read the branch into the GitBranch itemGroup-->
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <ReadLinesFromFile File="$(BranchFile)">
<PropertyGroup> <Output TaskParameter="Lines" ItemName="GitBranch" />
<BuildBranch>@(GitBranch)</BuildBranch> </ReadLinesFromFile>
</PropertyGroup> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
</Target> <PropertyGroup>
<BuildBranch>@(GitBranch)</BuildBranch>
<Target Name="WriteGitRevision" BeforeTargets="CoreCompile"> </PropertyGroup>
<!-- names the obj/.../CustomAssemblyInfo.cs file --> </Target>
<PropertyGroup>
<CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile> <Target Name="WriteGitRevision" BeforeTargets="CoreCompile">
</PropertyGroup> <!-- names the obj/.../CustomAssemblyInfo.cs file -->
<!-- includes the CustomAssemblyInfo for compilation into your project --> <PropertyGroup>
<ItemGroup> <CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile>
<Compile Include="$(CustomAssemblyInfoFile)" /> </PropertyGroup>
</ItemGroup> <!-- includes the CustomAssemblyInfo for compilation into your project -->
<!-- defines the AssemblyMetadata attribute that will be written --> <ItemGroup>
<ItemGroup> <Compile Include="$(CustomAssemblyInfoFile)" />
<AssemblyAttributes Include="AssemblyMetadata"> </ItemGroup>
<_Parameter1>GitRevision</_Parameter1> <!-- defines the AssemblyMetadata attribute that will be written -->
<_Parameter2>$(BuildHash)</_Parameter2> <ItemGroup>
</AssemblyAttributes> <AssemblyAttributes Include="AssemblyMetadata">
<AssemblyAttributes Include="AssemblyMetadata"> <_Parameter1>GitRevision</_Parameter1>
<_Parameter1>GitBranch</_Parameter1> <_Parameter2>$(BuildHash)</_Parameter2>
<_Parameter2>$(BuildBranch)</_Parameter2> </AssemblyAttributes>
</AssemblyAttributes> <AssemblyAttributes Include="AssemblyMetadata">
<AssemblyAttributes Include="AssemblyMetadata"> <_Parameter1>GitBranch</_Parameter1>
<_Parameter1>ProjectDir</_Parameter1> <_Parameter2>$(BuildBranch)</_Parameter2>
<_Parameter2>$(ProjectDir)</_Parameter2> </AssemblyAttributes>
</AssemblyAttributes> <AssemblyAttributes Include="AssemblyMetadata">
</ItemGroup> <_Parameter1>ProjectDir</_Parameter1>
<!-- writes the attribute to the customAssemblyInfo file --> <_Parameter2>$(ProjectDir)</_Parameter2>
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" /> </AssemblyAttributes>
</Target> </ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file -->
</Project> <WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
</Target>
</Project>

View File

@@ -1,154 +1,157 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.15.15.0</Version> <Version>0.15.17.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon> <ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
<ReleaseVersion>0.9.0.0</ReleaseVersion> <ReleaseVersion>0.9.0.0</ReleaseVersion>
<Configurations>Debug;Release;Unstable</Configurations> <Configurations>Debug;Release;Unstable</Configurations>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;SERVER;OSX;USE_STEAM;DEBUG;NETCOREAPP;NETCOREAPP3_0</DefineConstants> <DefineConstants>TRACE;SERVER;OSX;USE_STEAM;DEBUG;NETCOREAPP;NETCOREAPP3_0</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\DebugMac</OutputPath> <OutputPath>..\bin\DebugMac</OutputPath>
<ConsolePause>true</ConsolePause> <ConsolePause>true</ConsolePause>
<CheckForOverflowUnderflow></CheckForOverflowUnderflow> <CheckForOverflowUnderflow></CheckForOverflowUnderflow>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DefineConstants>TRACE;DEBUG;SERVER;OSX;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;DEBUG;SERVER;OSX;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Mac\</OutputPath> <OutputPath>..\bin\$(Configuration)Mac\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0</DefineConstants> <DefineConstants>TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<DebugType></DebugType> <DebugType></DebugType>
<OutputPath>..\bin\ReleaseMac</OutputPath> <OutputPath>..\bin\ReleaseMac</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'">
<DefineConstants>TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0;UNSTABLE</DefineConstants> <DefineConstants>TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0;UNSTABLE</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<DebugType /> <DebugType />
<OutputPath>..\bin\ReleaseMac</OutputPath> <OutputPath>..\bin\ReleaseMac</OutputPath>
<Optimize>true</Optimize> <Optimize>true</Optimize>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DefineConstants>TRACE;SERVER;OSX;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;OSX;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Mac\</OutputPath> <OutputPath>..\bin\$(Configuration)Mac\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'">
<DefineConstants>TRACE;SERVER;OSX;X64;USE_STEAM;UNSTABLE</DefineConstants> <DefineConstants>TRACE;SERVER;OSX;X64;USE_STEAM;UNSTABLE</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Mac\</OutputPath> <OutputPath>..\bin\$(Configuration)Mac\</OutputPath>
<Optimize>true</Optimize> <Optimize>true</Optimize>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.props" />
<Content Remove="..\BarotraumaShared\libsteam_api64.dylib" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
<Content Remove="..\BarotraumaShared\libsteam_api64.so" /> <Content Remove="..\BarotraumaShared\libsteam_api64.dylib" />
<Content Remove="DedicatedServer.exe" /> <Content Remove="..\BarotraumaShared\libsteam_api64.so" />
</ItemGroup> <Content Remove="DedicatedServer.exe" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'!='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" /> <ItemGroup Condition="'$(Configuration)'!='Debug'">
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> </ItemGroup>
</ItemGroup>
<ItemGroup>
<!-- Sourced from https://stackoverflow.com/a/45248069 --> <PackageReference Include="RestSharp" Version="106.13.0" />
<ItemGroup> </ItemGroup>
<None Include="..\BarotraumaShared\libsteam_api64.dylib">
<Link>libsteam_api64.dylib</Link> <!-- Sourced from https://stackoverflow.com/a/45248069 -->
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <ItemGroup>
</None> <None Include="..\BarotraumaShared\libsteam_api64.dylib">
</ItemGroup> <Link>libsteam_api64.dylib</Link>
<Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<PropertyGroup> </None>
<!-- temp file for the git version (lives in "obj" folder)--> </ItemGroup>
<VerFile>$(IntermediateOutputPath)gitver</VerFile> <Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''">
<BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile> <PropertyGroup>
</PropertyGroup> <!-- temp file for the git version (lives in "obj" folder)-->
<VerFile>$(IntermediateOutputPath)gitver</VerFile>
<!-- write the hash to the temp file.--> <BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile>
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true"> </PropertyGroup>
<Output TaskParameter="exitcode" ItemName="exitcodes" />
</Exec> <!-- write the hash to the temp file.-->
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" /> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true">
<Output TaskParameter="exitcode" ItemName="exitcodes" />
<Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" /> </Exec>
<Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" /> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" />
<!-- read the version into the GitVersion itemGroup--> <Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<ReadLinesFromFile File="$(VerFile)"> <Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<Output TaskParameter="Lines" ItemName="GitVersion" />
</ReadLinesFromFile> <!-- read the version into the GitVersion itemGroup-->
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <ReadLinesFromFile File="$(VerFile)">
<PropertyGroup> <Output TaskParameter="Lines" ItemName="GitVersion" />
<BuildHash>@(GitVersion)</BuildHash> </ReadLinesFromFile>
</PropertyGroup> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
<PropertyGroup>
<!-- read the branch into the GitBranch itemGroup--> <BuildHash>@(GitVersion)</BuildHash>
<ReadLinesFromFile File="$(BranchFile)"> </PropertyGroup>
<Output TaskParameter="Lines" ItemName="GitBranch" />
</ReadLinesFromFile> <!-- read the branch into the GitBranch itemGroup-->
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <ReadLinesFromFile File="$(BranchFile)">
<PropertyGroup> <Output TaskParameter="Lines" ItemName="GitBranch" />
<BuildBranch>@(GitBranch)</BuildBranch> </ReadLinesFromFile>
</PropertyGroup> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
</Target> <PropertyGroup>
<BuildBranch>@(GitBranch)</BuildBranch>
<Target Name="WriteGitRevision" BeforeTargets="CoreCompile"> </PropertyGroup>
<!-- names the obj/.../CustomAssemblyInfo.cs file --> </Target>
<PropertyGroup>
<CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile> <Target Name="WriteGitRevision" BeforeTargets="CoreCompile">
</PropertyGroup> <!-- names the obj/.../CustomAssemblyInfo.cs file -->
<!-- includes the CustomAssemblyInfo for compilation into your project --> <PropertyGroup>
<ItemGroup> <CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile>
<Compile Include="$(CustomAssemblyInfoFile)" /> </PropertyGroup>
</ItemGroup> <!-- includes the CustomAssemblyInfo for compilation into your project -->
<!-- defines the AssemblyMetadata attribute that will be written --> <ItemGroup>
<ItemGroup> <Compile Include="$(CustomAssemblyInfoFile)" />
<AssemblyAttributes Include="AssemblyMetadata"> </ItemGroup>
<_Parameter1>GitRevision</_Parameter1> <!-- defines the AssemblyMetadata attribute that will be written -->
<_Parameter2>$(BuildHash)</_Parameter2> <ItemGroup>
</AssemblyAttributes> <AssemblyAttributes Include="AssemblyMetadata">
<AssemblyAttributes Include="AssemblyMetadata"> <_Parameter1>GitRevision</_Parameter1>
<_Parameter1>GitBranch</_Parameter1> <_Parameter2>$(BuildHash)</_Parameter2>
<_Parameter2>$(BuildBranch)</_Parameter2> </AssemblyAttributes>
</AssemblyAttributes> <AssemblyAttributes Include="AssemblyMetadata">
<AssemblyAttributes Include="AssemblyMetadata"> <_Parameter1>GitBranch</_Parameter1>
<_Parameter1>ProjectDir</_Parameter1> <_Parameter2>$(BuildBranch)</_Parameter2>
<_Parameter2>$(ProjectDir)</_Parameter2> </AssemblyAttributes>
</AssemblyAttributes> <AssemblyAttributes Include="AssemblyMetadata">
</ItemGroup> <_Parameter1>ProjectDir</_Parameter1>
<!-- writes the attribute to the customAssemblyInfo file --> <_Parameter2>$(ProjectDir)</_Parameter2>
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" /> </AssemblyAttributes>
</Target> </ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file -->
</Project> <WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
</Target>
</Project>

View File

@@ -269,7 +269,7 @@ namespace Barotrauma
{ {
string errorMsg = "Failed to write input to command line (window width: " + Console.WindowWidth + ", window height: " + Console.WindowHeight + ")\n" string errorMsg = "Failed to write input to command line (window width: " + Console.WindowWidth + ", window height: " + Console.WindowHeight + ")\n"
+ e.Message + "\n" + e.StackTrace.CleanupStackTrace(); + e.Message + "\n" + e.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("DebugConsole.RewriteInputToCommandLine", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("DebugConsole.RewriteInputToCommandLine", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
} }
} }
@@ -1588,10 +1588,6 @@ namespace Barotrauma
if (tpCharacter != null) if (tpCharacter != null)
{ {
tpCharacter.TeleportTo(cursorWorldPos); tpCharacter.TeleportTo(cursorWorldPos);
if (tpCharacter.AIController?.SteeringManager is IndoorsSteeringManager pathSteering)
{
pathSteering.ResetPath();
}
} }
} }
); );

View File

@@ -1,7 +1,6 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Barotrauma.Steam; using Barotrauma.Steam;
using FarseerPhysics.Dynamics; using FarseerPhysics.Dynamics;
using GameAnalyticsSDK.Net;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -98,8 +97,9 @@ namespace Barotrauma
Console.WriteLine("Initializing SteamManager"); Console.WriteLine("Initializing SteamManager");
SteamManager.Initialize(); SteamManager.Initialize();
Console.WriteLine("Initializing GameAnalytics"); //TODO: figure out how consent is supposed to work for servers
if (GameSettings.SendUserStatistics) GameAnalyticsManager.Init(); //Console.WriteLine("Initializing GameAnalytics");
//GameAnalyticsManager.InitIfConsented();
Console.WriteLine("Initializing GameScreen"); Console.WriteLine("Initializing GameScreen");
GameScreen = new GameScreen(); GameScreen = new GameScreen();
@@ -418,7 +418,7 @@ namespace Barotrauma
SaveUtil.CleanUnnecessarySaveFiles(); SaveUtil.CleanUnnecessarySaveFiles();
if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); } if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); }
if (GameSettings.SendUserStatistics) { GameAnalytics.OnQuit(); } if (GameAnalyticsManager.SendUserStatistics) { GameAnalyticsManager.ShutDown(); }
MainThread = null; MainThread = null;
} }

View File

@@ -13,7 +13,7 @@ namespace Barotrauma
get { return itemData != null; } get { return itemData != null; }
} }
public CharacterCampaignData(Client client, bool giveRespawnPenaltyAffliction = false) public CharacterCampaignData(Client client)
{ {
Name = client.Name; Name = client.Name;
ClientEndPoint = client.Connection.EndPointString; ClientEndPoint = client.Connection.EndPointString;
@@ -22,13 +22,6 @@ namespace Barotrauma
healthData = new XElement("health"); healthData = new XElement("health");
client.Character?.CharacterHealth?.Save(healthData); client.Character?.CharacterHealth?.Save(healthData);
if (giveRespawnPenaltyAffliction)
{
var respawnPenaltyAffliction = RespawnManager.GetRespawnPenaltyAffliction();
healthData.Add(new XElement("Affliction",
new XAttribute("identifier", respawnPenaltyAffliction.Identifier),
new XAttribute("strength", respawnPenaltyAffliction.Strength.ToString("G", CultureInfo.InvariantCulture))));
}
if (client.Character?.Inventory != null) if (client.Character?.Inventory != null)
{ {
itemData = new XElement("inventory"); itemData = new XElement("inventory");

View File

@@ -211,18 +211,6 @@ namespace Barotrauma
{ {
c.Character = null; c.Character = null;
} }
if (c.HasSpawned && c.CharacterInfo != null && c.CharacterInfo.CauseOfDeath != null && c.CharacterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected)
{
//the client has opted to spawn this round with Reaper's Tax
if (c.WaitForNextRoundRespawn.HasValue && !c.WaitForNextRoundRespawn.Value)
{
c.CharacterInfo.StartItemsGiven = false;
characterData.RemoveAll(cd => cd.MatchesClient(c));
characterData.Add(new CharacterCampaignData(c, giveRespawnPenaltyAffliction: true));
continue;
}
}
//use the info of the character the client is currently controlling //use the info of the character the client is currently controlling
// or the previously saved info if not (e.g. if the client has been spectating or died) // or the previously saved info if not (e.g. if the client has been spectating or died)
var characterInfo = c.Character?.Info ?? characterData.Find(d => d.MatchesClient(c))?.CharacterInfo; var characterInfo = c.Character?.Info ?? characterData.Find(d => d.MatchesClient(c))?.CharacterInfo;
@@ -231,6 +219,7 @@ namespace Barotrauma
if (characterInfo.CauseOfDeath != null && characterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected) if (characterInfo.CauseOfDeath != null && characterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected)
{ {
RespawnManager.ReduceCharacterSkills(characterInfo); RespawnManager.ReduceCharacterSkills(characterInfo);
characterInfo.RemoveSavedStatValuesOnDeath();
} }
c.CharacterInfo = characterInfo; c.CharacterInfo = characterInfo;
characterData.RemoveAll(cd => cd.MatchesClient(c)); characterData.RemoveAll(cd => cd.MatchesClient(c));
@@ -358,6 +347,7 @@ namespace Barotrauma
} }
} }
} }
UpdateCampaignSubs();
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
PendingSubmarineSwitch = null; PendingSubmarineSwitch = null;
@@ -400,20 +390,50 @@ namespace Barotrauma
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
if (GameMain.Server != null) CargoManager.OnItemsInBuyCrateChanged += () => { LastUpdateID++; };
{ CargoManager.OnPurchasedItemsChanged += () => { LastUpdateID++; };
CargoManager.OnItemsInBuyCrateChanged += () => { LastUpdateID++; }; CargoManager.OnSoldItemsChanged += () => { LastUpdateID++; };
CargoManager.OnPurchasedItemsChanged += () => { LastUpdateID++; }; UpgradeManager.OnUpgradesChanged += () => { LastUpdateID++; };
CargoManager.OnSoldItemsChanged += () => { LastUpdateID++; }; Map.OnLocationSelected += (loc, connection) => { LastUpdateID++; };
UpgradeManager.OnUpgradesChanged += () => { LastUpdateID++; }; Map.OnMissionsSelected += (loc, mission) => { LastUpdateID++; };
Map.OnLocationSelected += (loc, connection) => { LastUpdateID++; }; Reputation.OnAnyReputationValueChanged += () => { LastUpdateID++; };
Map.OnMissionsSelected += (loc, mission) => { LastUpdateID++; };
Reputation.OnAnyReputationValueChanged += () => { LastUpdateID++; }; UpdateCampaignSubs();
}
//increment save ID so clients know they're lacking the most up-to-date save file //increment save ID so clients know they're lacking the most up-to-date save file
LastSaveID++; LastSaveID++;
} }
private void UpdateCampaignSubs()
{
bool isSubmarineVisible(SubmarineInfo s)
=> !GameMain.Server.ServerSettings.HiddenSubs.Any(h
=> s.Name.Equals(h, StringComparison.OrdinalIgnoreCase));
List<SubmarineInfo> availableSubs =
SubmarineInfo.SavedSubmarines
.Where(s =>
s.IsCampaignCompatible
&& isSubmarineVisible(s))
.ToList();
if (!availableSubs.Any())
{
//None of the available subs were marked as campaign-compatible, just include all visible subs
availableSubs.AddRange(
SubmarineInfo.SavedSubmarines
.Where(isSubmarineVisible));
}
if (!availableSubs.Any())
{
//No subs are visible at all! Just make the selected one available
availableSubs.Add(GameMain.NetLobbyScreen.SelectedSub);
}
GameMain.NetLobbyScreen.CampaignSubmarines = availableSubs;
}
public void DiscardClientCharacterData(Client client) public void DiscardClientCharacterData(Client client)
{ {
characterData.RemoveAll(cd => cd.MatchesClient(client)); characterData.RemoveAll(cd => cd.MatchesClient(client));
@@ -1030,14 +1050,6 @@ namespace Barotrauma
new XAttribute("points", savedExperiencePoint.ExperiencePoints))); new XAttribute("points", savedExperiencePoint.ExperiencePoints)));
} }
// save available submarines
XElement availableSubsElement = new XElement("AvailableSubs");
for (int i = 0; i < GameMain.NetLobbyScreen.CampaignSubmarines.Count; i++)
{
availableSubsElement.Add(new XElement("Sub", new XAttribute("name", GameMain.NetLobbyScreen.CampaignSubmarines[i].Name)));
}
modeElement.Add(availableSubsElement);
element.Add(modeElement); element.Add(modeElement);
//save character data to a separate file //save character data to a separate file

View File

@@ -14,6 +14,7 @@ namespace Barotrauma.Items.Components
{ {
if (c.Character == null) { return; } if (c.Character == null) { return; }
var requestedFixAction = (FixActions)msg.ReadRangedInteger(0, 2); var requestedFixAction = (FixActions)msg.ReadRangedInteger(0, 2);
var QTESuccess = msg.ReadBoolean();
if (requestedFixAction != FixActions.None) if (requestedFixAction != FixActions.None)
{ {
if (!c.Character.IsTraitor && requestedFixAction == FixActions.Sabotage) if (!c.Character.IsTraitor && requestedFixAction == FixActions.Sabotage)
@@ -31,6 +32,11 @@ namespace Barotrauma.Items.Components
item.CreateServerEvent(this); item.CreateServerEvent(this);
} }
} }
else
{
RepairBoost(QTESuccess);
item.CreateServerEvent(this);
}
} }
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null) public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
@@ -42,6 +48,7 @@ namespace Barotrauma.Items.Components
msg.Write(tinkeringStrength); msg.Write(tinkeringStrength);
msg.Write(CurrentFixer == null ? (ushort)0 : CurrentFixer.ID); msg.Write(CurrentFixer == null ? (ushort)0 : CurrentFixer.ID);
msg.WriteRangedInteger((int)currentFixerAction, 0, 2); msg.WriteRangedInteger((int)currentFixerAction, 0, 2);
msg.Write(repairBoost);
} }
} }
} }

View File

@@ -41,7 +41,7 @@ namespace Barotrauma
} }
msg.WriteRangedInteger((int)NetEntityEvent.Type.Invalid, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1); msg.WriteRangedInteger((int)NetEntityEvent.Type.Invalid, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
DebugConsole.Log(errorMsg); DebugConsole.Log(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Item.ServerWrite:InvalidData" + Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("Item.ServerWrite:InvalidData" + Name, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
@@ -186,7 +186,7 @@ namespace Barotrauma
msg.LengthBits = initialWritePos; msg.LengthBits = initialWritePos;
msg.WriteRangedInteger((int)NetEntityEvent.Type.Invalid, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1); msg.WriteRangedInteger((int)NetEntityEvent.Type.Invalid, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
DebugConsole.Log(errorMsg); DebugConsole.Log(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Item.ServerWrite:" + errorMsg, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("Item.ServerWrite:" + errorMsg, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
} }
} }
@@ -402,7 +402,7 @@ namespace Barotrauma
{ {
string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet.\n" + Environment.StackTrace.CleanupStackTrace(); string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet.\n" + Environment.StackTrace.CleanupStackTrace();
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Item.CreateServerEvent:EventForUninitializedItem" + Name + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("Item.CreateServerEvent:EventForUninitializedItem" + Name + ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
@@ -423,7 +423,7 @@ namespace Barotrauma
{ {
string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet.\n" + Environment.StackTrace.CleanupStackTrace(); string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet.\n" + Environment.StackTrace.CleanupStackTrace();
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Item.CreateServerEvent:EventForUninitializedItem" + Name + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("Item.CreateServerEvent:EventForUninitializedItem" + Name + ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }

View File

@@ -274,6 +274,8 @@ namespace Barotrauma.Networking
public void Save() public void Save()
{ {
GameServer.Log("Saving banlist", ServerLog.MessageType.ServerMessage); GameServer.Log("Saving banlist", ServerLog.MessageType.ServerMessage);
GameMain.Server?.ServerSettings?.UpdateFlag(ServerSettings.NetFlags.Properties);
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value); bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
@@ -344,7 +346,7 @@ namespace Barotrauma.Networking
catch (Exception e) catch (Exception e)
{ {
string errorMsg = "Error while writing banlist. {" + e + "}\n" + e.StackTrace.CleanupStackTrace(); string errorMsg = "Error while writing banlist. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("Banlist.ServerAdminWrite", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("Banlist.ServerAdminWrite", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw; throw;
} }
} }

View File

@@ -278,7 +278,7 @@ namespace Barotrauma.Networking
DebugConsole.ThrowError("FileSender threw an exception when trying to send data", e); DebugConsole.ThrowError("FileSender threw an exception when trying to send data", e);
GameAnalyticsManager.AddErrorEventOnce( GameAnalyticsManager.AddErrorEventOnce(
"FileSender.Update:Exception", "FileSender.Update:Exception",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
"FileSender threw an exception when trying to send data:\n" + e.Message + "\n" + e.StackTrace.CleanupStackTrace()); "FileSender threw an exception when trying to send data:\n" + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
transfer.Status = FileTransferStatus.Error; transfer.Status = FileTransferStatus.Error;
return; return;

View File

@@ -600,16 +600,19 @@ namespace Barotrauma.Networking
//constantly increase AFK timer if the client is controlling a character (gets reset to zero every time an input is received) //constantly increase AFK timer if the client is controlling a character (gets reset to zero every time an input is received)
if (gameStarted && c.Character != null && !c.Character.IsDead && !c.Character.IsIncapacitated) if (gameStarted && c.Character != null && !c.Character.IsDead && !c.Character.IsIncapacitated)
{ {
if (c.Connection != OwnerConnection) c.KickAFKTimer += deltaTime; if (c.Connection != OwnerConnection && c.Permissions != ClientPermissions.All) { c.KickAFKTimer += deltaTime; }
} }
} }
IEnumerable<Client> kickAFK = connectedClients.FindAll(c => if (connectedClients.Any(c => c.KickAFKTimer >= serverSettings.KickAFKTime))
c.KickAFKTimer >= serverSettings.KickAFKTime &&
(OwnerConnection == null || c.Connection != OwnerConnection));
foreach (Client c in kickAFK)
{ {
KickClient(c, "DisconnectMessage.AFK"); IEnumerable<Client> kickAFK = connectedClients.FindAll(c =>
c.KickAFKTimer >= serverSettings.KickAFKTime &&
(OwnerConnection == null || c.Connection != OwnerConnection));
foreach (Client c in kickAFK)
{
KickClient(c, "DisconnectMessage.AFK");
}
} }
serverPeer.Update(deltaTime); serverPeer.Update(deltaTime);
@@ -632,7 +635,7 @@ namespace Barotrauma.Networking
{ {
DebugConsole.ThrowError("Failed to write a network message for the client \"" + c.Name + "\"!", e); DebugConsole.ThrowError("Failed to write a network message for the client \"" + c.Name + "\"!", e);
string errorMsg = "Failed to write a network message for the client \"" + c.Name + "\"! (MidRoundSyncing: " + c.NeedsMidRoundSync + ")\n" string errorMsg = "Failed to write a network message for a client! (MidRoundSyncing: " + c.NeedsMidRoundSync + ")\n"
+ e.Message + "\n" + e.StackTrace.CleanupStackTrace(); + e.Message + "\n" + e.StackTrace.CleanupStackTrace();
if (e.InnerException != null) if (e.InnerException != null)
{ {
@@ -641,7 +644,7 @@ namespace Barotrauma.Networking
GameAnalyticsManager.AddErrorEventOnce( GameAnalyticsManager.AddErrorEventOnce(
"GameServer.Update:ClientWriteFailed" + e.StackTrace.CleanupStackTrace(), "GameServer.Update:ClientWriteFailed" + e.StackTrace.CleanupStackTrace(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
errorMsg); errorMsg);
} }
} }
@@ -791,6 +794,8 @@ namespace Barotrauma.Networking
string localSavePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName); string localSavePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName);
if (connectedClient.HasPermission(ClientPermissions.SelectMode) || connectedClient.HasPermission(ClientPermissions.ManageCampaign)) if (connectedClient.HasPermission(ClientPermissions.SelectMode) || connectedClient.HasPermission(ClientPermissions.ManageCampaign))
{ {
ServerSettings.RadiationEnabled = settings.RadiationEnabled;
ServerSettings.MaxMissionCount = settings.MaxMissionCount;
MultiPlayerCampaign.StartNewCampaign(localSavePath, matchingSub.FilePath, seed, settings); MultiPlayerCampaign.StartNewCampaign(localSavePath, matchingSub.FilePath, seed, settings);
} }
} }
@@ -854,6 +859,7 @@ namespace Barotrauma.Networking
private void HandleClientError(IReadMessage inc, Client c) private void HandleClientError(IReadMessage inc, Client c)
{ {
string errorStr = "Unhandled error report"; string errorStr = "Unhandled error report";
string errorStrNoName = errorStr;
ClientNetError error = (ClientNetError)inc.ReadByte(); ClientNetError error = (ClientNetError)inc.ReadByte();
switch (error) switch (error)
@@ -861,7 +867,7 @@ namespace Barotrauma.Networking
case ClientNetError.MISSING_EVENT: case ClientNetError.MISSING_EVENT:
UInt16 expectedID = inc.ReadUInt16(); UInt16 expectedID = inc.ReadUInt16();
UInt16 receivedID = inc.ReadUInt16(); UInt16 receivedID = inc.ReadUInt16();
errorStr = "Expecting event id " + expectedID.ToString() + ", received " + receivedID.ToString(); errorStr = errorStrNoName = "Expecting event id " + expectedID.ToString() + ", received " + receivedID.ToString();
break; break;
case ClientNetError.MISSING_ENTITY: case ClientNetError.MISSING_ENTITY:
UInt16 eventID = inc.ReadUInt16(); UInt16 eventID = inc.ReadUInt16();
@@ -869,25 +875,26 @@ namespace Barotrauma.Networking
Entity entity = Entity.FindEntityByID(entityID); Entity entity = Entity.FindEntityByID(entityID);
if (entity == null) if (entity == null)
{ {
errorStr = "Received an update for an entity that doesn't exist (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ")."; errorStr = errorStrNoName = "Received an update for an entity that doesn't exist (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
} }
else if (entity is Character character) else if (entity is Character character)
{ {
errorStr = "Missing character " + character.Name + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ")."; errorStr = "Missing character " + character.Name + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
errorStrNoName = "Missing character " + character.SpeciesName + "(event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
} }
else if (entity is Item item) else if (entity is Item item)
{ {
errorStr = "Missing item " + item.Name + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ")."; errorStr = errorStrNoName = "Missing item " + item.Name + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
} }
else else
{ {
errorStr = "Missing entity " + entity.ToString() + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ")."; errorStr = errorStrNoName = "Missing entity " + entity.ToString() + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
} }
break; break;
} }
Log(GameServer.ClientLogName(c) + " has reported an error: " + errorStr, ServerLog.MessageType.Error); Log(ClientLogName(c) + " has reported an error: " + errorStr, ServerLog.MessageType.Error);
GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:" + errorStr, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorStr); GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:" + errorStrNoName, GameAnalyticsManager.ErrorSeverity.Error, errorStr);
try try
{ {
@@ -1392,49 +1399,23 @@ namespace Barotrauma.Networking
} }
break; break;
case ClientPermissions.SelectSub: case ClientPermissions.SelectSub:
bool isCampaign = inc.ReadBoolean(); bool isShuttle = inc.ReadBoolean();
if (!isCampaign) inc.ReadPadBits();
UInt16 subIndex = inc.ReadUInt16();
var subList = GameMain.NetLobbyScreen.GetSubList();
if (subIndex >= subList.Count)
{ {
bool isShuttle = inc.ReadBoolean(); DebugConsole.NewMessage($"Client \"{ClientLogName(sender)}\" attempted to select a sub, index out of bounds ({subIndex})", Color.Red);
inc.ReadPadBits();
UInt16 subIndex = inc.ReadUInt16();
var subList = GameMain.NetLobbyScreen.GetSubList();
if (subIndex >= subList.Count)
{
DebugConsole.NewMessage("Client \"" + GameServer.ClientLogName(sender) + "\" attempted to select a sub, index out of bounds (" + subIndex + ")", Color.Red);
}
else
{
if (isShuttle)
{
GameMain.NetLobbyScreen.SelectedShuttle = subList[subIndex];
}
else
{
GameMain.NetLobbyScreen.SelectedSub = subList[subIndex];
}
}
} }
else else
{ {
int subEqualityCheckVal = inc.ReadInt32(); if (isShuttle)
bool add = inc.ReadBoolean();
SubmarineInfo sub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.EqualityCheckVal == subEqualityCheckVal);
if (sub == null)
{ {
DebugConsole.NewMessage("Client \"" + GameServer.ClientLogName(sender) + "\" attempted to select a sub that does not exist on the server!", Color.Red); GameMain.NetLobbyScreen.SelectedShuttle = subList[subIndex];
} }
else else
{ {
if (add) GameMain.NetLobbyScreen.SelectedSub = subList[subIndex];
{
GameMain.NetLobbyScreen.AddCampaignSubmarine(sub);
}
else
{
GameMain.NetLobbyScreen.RemoveCampaignSubmarine(sub);
}
} }
} }
break; break;
@@ -1751,7 +1732,7 @@ namespace Barotrauma.Networking
" Chat message size: " + chatMessageBytes + " bytes\n" + " Chat message size: " + chatMessageBytes + " bytes\n" +
" Position update size: " + positionUpdateBytes + " bytes\n\n"; " Position update size: " + positionUpdateBytes + " bytes\n\n";
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
} }
serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable); serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable);
@@ -1791,7 +1772,7 @@ namespace Barotrauma.Networking
} }
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame2:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame2:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
} }
serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable); serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable);
@@ -1878,7 +1859,7 @@ namespace Barotrauma.Networking
List<int> campaignSubIndices = new List<int>(); List<int> campaignSubIndices = new List<int>();
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign) if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
{ {
List<SubmarineInfo> subList = GameMain.NetLobbyScreen.GetSubList(); IReadOnlyList<SubmarineInfo> subList = GameMain.NetLobbyScreen.GetSubList();
for (int i = 0; i < subList.Count; i++) for (int i = 0; i < subList.Count; i++)
{ {
if (GameMain.NetLobbyScreen.CampaignSubmarines.Contains(subList[i])) if (GameMain.NetLobbyScreen.CampaignSubmarines.Contains(subList[i]))
@@ -1916,9 +1897,6 @@ namespace Barotrauma.Networking
{ {
outmsg.Write(autoRestartTimerRunning ? serverSettings.AutoRestartTimer : 0.0f); outmsg.Write(autoRestartTimerRunning ? serverSettings.AutoRestartTimer : 0.0f);
} }
outmsg.Write(serverSettings.RadiationEnabled);
outmsg.Write((byte)serverSettings.MaxMissionCount);
} }
else else
{ {
@@ -1958,8 +1936,31 @@ namespace Barotrauma.Networking
chatMessageBytes = outmsg.LengthBytes - outmsg.LengthBytes; chatMessageBytes = outmsg.LengthBytes - outmsg.LengthBytes;
outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE); outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE);
if (isInitialUpdate) bool messageTooLarge = outmsg.LengthBytes > MsgConstants.MTU;
if (messageTooLarge && !isInitialUpdate)
{
string warningMsg = "Maximum packet size exceeded, will send using reliable mode (" + outmsg.LengthBytes + " > " + MsgConstants.MTU + ")\n";
warningMsg +=
" Client list size: " + clientListBytes + " bytes\n" +
" Chat message size: " + chatMessageBytes + " bytes\n" +
" Campaign size: " + campaignBytes + " bytes\n" +
" Settings size: " + settingsBytes + " bytes\n";
if (initialUpdateBytes > 0)
{
warningMsg +=
" Initial update size: " + settingsBuf.LengthBytes + " bytes\n";
}
if (settingsBuf != null)
{
warningMsg +=
" Settings buffer size: " + settingsBuf.LengthBytes + " bytes\n";
}
if (GameSettings.VerboseLogging) { DebugConsole.AddWarning(warningMsg); }
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:ClientWriteLobby" + outmsg.LengthBytes, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
}
if (isInitialUpdate || messageTooLarge)
{ {
//the initial update may be very large if the host has a large number //the initial update may be very large if the host has a large number
//of submarine files, so the message may have to be fragmented //of submarine files, so the message may have to be fragmented
@@ -1975,28 +1976,6 @@ namespace Barotrauma.Networking
} }
else else
{ {
if (outmsg.LengthBytes > MsgConstants.MTU)
{
string errorMsg = "Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + MsgConstants.MTU + ")\n";
errorMsg +=
" Client list size: " + clientListBytes + " bytes\n" +
" Chat message size: " + chatMessageBytes + " bytes\n" +
" Campaign size: " + campaignBytes + " bytes\n" +
" Settings size: " + settingsBytes + " bytes\n";
if (initialUpdateBytes > 0)
{
errorMsg +=
" Initial update size: " + settingsBuf.LengthBytes + " bytes\n";
}
if (settingsBuf != null)
{
errorMsg +=
" Settings buffer size: " + settingsBuf.LengthBytes + " bytes\n";
}
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:ClientWriteLobby" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable); serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable);
} }
} }
@@ -2121,7 +2100,7 @@ namespace Barotrauma.Networking
startGameCoroutine = null; startGameCoroutine = null;
string errorMsg = "Starting the round failed. Campaign was still active, but the map has been disposed. Try selecting another game mode."; string errorMsg = "Starting the round failed. Campaign was still active, but the map has been disposed. Try selecting another game mode.";
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.StartGame:InvalidCampaignState", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameServer.StartGame:InvalidCampaignState", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
if (OwnerConnection != null) if (OwnerConnection != null)
{ {
SendDirectChatMessage(errorMsg, connectedClients.Find(c => c.Connection == OwnerConnection), ChatMessageType.Error); SendDirectChatMessage(errorMsg, connectedClients.Find(c => c.Connection == OwnerConnection), ChatMessageType.Error);
@@ -2163,7 +2142,7 @@ namespace Barotrauma.Networking
{ {
string errorMsg = "Failed to start a campaign round (next level not set)."; string errorMsg = "Failed to start a campaign round (next level not set).";
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.StartGame:InvalidCampaignState", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameServer.StartGame:InvalidCampaignState", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
if (OwnerConnection != null) if (OwnerConnection != null)
{ {
SendDirectChatMessage(errorMsg, connectedClients.Find(c => c.Connection == OwnerConnection), ChatMessageType.Error); SendDirectChatMessage(errorMsg, connectedClients.Find(c => c.Connection == OwnerConnection), ChatMessageType.Error);
@@ -2392,6 +2371,9 @@ namespace Barotrauma.Networking
spawnedCharacter.TeamID = teamID; spawnedCharacter.TeamID = teamID;
spawnedCharacter.GiveJobItems(mainSubWaypoints[i]); spawnedCharacter.GiveJobItems(mainSubWaypoints[i]);
spawnedCharacter.GiveIdCardTags(mainSubWaypoints[i]); spawnedCharacter.GiveIdCardTags(mainSubWaypoints[i]);
spawnedCharacter.Info.InventoryData = new XElement("inventory");
spawnedCharacter.Info.StartItemsGiven = true;
spawnedCharacter.SaveInventory();
// talents are only avilable for players in online sessions, but modders or someone else might want to have them loaded anyway // talents are only avilable for players in online sessions, but modders or someone else might want to have them loaded anyway
spawnedCharacter.LoadTalents(); spawnedCharacter.LoadTalents();
} }
@@ -2953,7 +2935,7 @@ namespace Barotrauma.Networking
{ {
string errorMsg = "Attempted to send a chat message to a null client.\n" + Environment.StackTrace.CleanupStackTrace(); string errorMsg = "Attempted to send a chat message to a null client.\n" + Environment.StackTrace.CleanupStackTrace();
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.SendDirectChatMessage:ClientNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameServer.SendDirectChatMessage:ClientNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
@@ -3255,7 +3237,7 @@ namespace Barotrauma.Networking
BanClient(c, "ServerMessage.KickedByVoteAutoBan", duration: TimeSpan.FromSeconds(serverSettings.AutoBanTime)); BanClient(c, "ServerMessage.KickedByVoteAutoBan", duration: TimeSpan.FromSeconds(serverSettings.AutoBanTime));
} }
GameMain.NetLobbyScreen.LastUpdateID++; //GameMain.NetLobbyScreen.LastUpdateID++;
SendVoteStatus(connectedClients); SendVoteStatus(connectedClients);

View File

@@ -186,7 +186,7 @@ namespace Barotrauma.Networking
DebugConsole.ThrowError(errorMsg, e); DebugConsole.ThrowError(errorMsg, e);
} }
GameAnalyticsManager.AddErrorEventOnce("ServerEntityEventManager.Read:ReadFailed" + entityName, GameAnalyticsManager.AddErrorEventOnce("ServerEntityEventManager.Read:ReadFailed" + entityName,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
"Failed to read server event for entity \"" + entityName + "\"!\n" + e.StackTrace.CleanupStackTrace()); "Failed to read server event for entity \"" + entityName + "\"!\n" + e.StackTrace.CleanupStackTrace());
} }

View File

@@ -125,7 +125,7 @@ namespace Barotrauma.Networking
catch (Exception e) catch (Exception e)
{ {
string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace.CleanupStackTrace(); string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("LidgrenServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("LidgrenServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
#if DEBUG #if DEBUG
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
#else #else
@@ -208,15 +208,13 @@ namespace Barotrauma.Networking
PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == inc.SenderConnection); PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == inc.SenderConnection);
byte incByte = inc.ReadByte(); PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
if (isConnectionInitializationStep && pendingClient != null) if (packetHeader.IsConnectionInitializationStep() && pendingClient != null)
{ {
ReadConnectionInitializationStep(pendingClient, new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false)); ReadConnectionInitializationStep(pendingClient, new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false));
} }
else if (!isConnectionInitializationStep) else if (!packetHeader.IsConnectionInitializationStep())
{ {
LidgrenConnection conn = connectedClients.Find(c => (c is LidgrenConnection l) && l.NetConnection == inc.SenderConnection) as LidgrenConnection; LidgrenConnection conn = connectedClients.Find(c => (c is LidgrenConnection l) && l.NetConnection == inc.SenderConnection) as LidgrenConnection;
if (conn == null) if (conn == null)
@@ -242,7 +240,7 @@ namespace Barotrauma.Networking
//DebugConsole.NewMessage(isCompressed + " " + isConnectionInitializationStep + " " + (int)incByte + " " + length); //DebugConsole.NewMessage(isCompressed + " " + isConnectionInitializationStep + " " + (int)incByte + " " + length);
IReadMessage msg = new ReadOnlyMessage(inc.Data, isCompressed, inc.PositionInBytes, length, conn); IReadMessage msg = new ReadOnlyMessage(inc.Data, packetHeader.IsCompressed(), inc.PositionInBytes, length, conn);
OnMessageReceived?.Invoke(conn, msg); OnMessageReceived?.Invoke(conn, msg);
} }
} }

View File

@@ -102,7 +102,7 @@ namespace Barotrauma.Networking
catch (Exception e) catch (Exception e)
{ {
string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace.CleanupStackTrace(); string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("SteamP2PServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("SteamP2PServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
#if DEBUG #if DEBUG
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
#else #else
@@ -125,14 +125,9 @@ namespace Barotrauma.Networking
UInt64 senderSteamId = inc.ReadUInt64(); UInt64 senderSteamId = inc.ReadUInt64();
UInt64 ownerSteamId = inc.ReadUInt64(); UInt64 ownerSteamId = inc.ReadUInt64();
byte incByte = inc.ReadByte(); PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0;
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;
if (isServerMessage) if (packetHeader.IsServerMessage())
{ {
DebugConsole.ThrowError("Got server message from" + senderSteamId.ToString()); DebugConsole.ThrowError("Got server message from" + senderSteamId.ToString());
return; return;
@@ -160,7 +155,7 @@ namespace Barotrauma.Networking
} }
return; return;
} }
else if (isDisconnectMessage) else if (packetHeader.IsDisconnectMessage())
{ {
if (pendingClient != null) if (pendingClient != null)
{ {
@@ -174,12 +169,12 @@ namespace Barotrauma.Networking
} }
return; return;
} }
else if (isHeartbeatMessage) else if (packetHeader.IsHeartbeatMessage())
{ {
//message exists solely as a heartbeat, ignore its contents //message exists solely as a heartbeat, ignore its contents
return; return;
} }
else if (isConnectionInitializationStep) else if (packetHeader.IsConnectionInitializationStep())
{ {
if (pendingClient != null) if (pendingClient != null)
@@ -203,7 +198,7 @@ namespace Barotrauma.Networking
{ {
UInt16 length = inc.ReadUInt16(); UInt16 length = inc.ReadUInt16();
IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, connectedClient); IReadMessage msg = new ReadOnlyMessage(inc.Buffer, packetHeader.IsCompressed(), inc.BytePosition, length, connectedClient);
OnMessageReceived?.Invoke(connectedClient, msg); OnMessageReceived?.Invoke(connectedClient, msg);
} }
} }
@@ -211,17 +206,17 @@ namespace Barotrauma.Networking
{ {
if (OwnerConnection != null) { (OwnerConnection as SteamP2PConnection).Heartbeat(); } if (OwnerConnection != null) { (OwnerConnection as SteamP2PConnection).Heartbeat(); }
if (isDisconnectMessage) if (packetHeader.IsDisconnectMessage())
{ {
DebugConsole.ThrowError("Received disconnect message from owner"); DebugConsole.ThrowError("Received disconnect message from owner");
return; return;
} }
if (isServerMessage) if (packetHeader.IsServerMessage())
{ {
DebugConsole.ThrowError("Received server message from owner"); DebugConsole.ThrowError("Received server message from owner");
return; return;
} }
if (isConnectionInitializationStep) if (packetHeader.IsConnectionInitializationStep())
{ {
if (OwnerConnection == null) if (OwnerConnection == null)
{ {
@@ -236,7 +231,7 @@ namespace Barotrauma.Networking
} }
return; return;
} }
if (isHeartbeatMessage) if (packetHeader.IsHeartbeatMessage())
{ {
return; return;
} }
@@ -244,7 +239,7 @@ namespace Barotrauma.Networking
{ {
UInt16 length = inc.ReadUInt16(); UInt16 length = inc.ReadUInt16();
IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, OwnerConnection); IReadMessage msg = new ReadOnlyMessage(inc.Buffer, packetHeader.IsCompressed(), inc.BytePosition, length, OwnerConnection);
OnMessageReceived?.Invoke(OwnerConnection, msg); OnMessageReceived?.Invoke(OwnerConnection, msg);
} }
} }
@@ -267,7 +262,7 @@ namespace Barotrauma.Networking
} }
IWriteMessage msgToSend = new WriteOnlyMessage(); IWriteMessage msgToSend = new WriteOnlyMessage();
byte[] msgData = new byte[msg.LengthBytes]; byte[] msgData = new byte[16];
msg.PrepareForSending(ref msgData, out bool isCompressed, out int length); msg.PrepareForSending(ref msgData, out bool isCompressed, out int length);
msgToSend.Write(conn.SteamID); msgToSend.Write(conn.SteamID);
msgToSend.Write((byte)deliveryMethod); msgToSend.Write((byte)deliveryMethod);

View File

@@ -398,6 +398,7 @@ namespace Barotrauma.Networking
else else
{ {
ReduceCharacterSkills(characterInfos[i]); ReduceCharacterSkills(characterInfos[i]);
characterInfos[i].RemoveSavedStatValuesOnDeath();
} }
} }
} }

View File

@@ -14,6 +14,17 @@ namespace Barotrauma.Networking
public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.xml"; public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.xml";
public static readonly char SubmarineSeparatorChar = '|'; public static readonly char SubmarineSeparatorChar = '|';
public readonly Dictionary<NetFlags, UInt16> LastUpdateIdForFlag = new Dictionary<NetFlags, UInt16>();
public void UpdateFlag(NetFlags flag)
=> LastUpdateIdForFlag[flag] = (UInt16)(GameMain.NetLobbyScreen.LastUpdateID + 1);
public NetFlags GetRequiredFlags(Client c)
=> LastUpdateIdForFlag.Keys
.Where(k => LastUpdateIdForFlag[k] > c.LastRecvLobbyUpdate)
.Concat(NetFlags.None.ToEnumerable()) //prevents InvalidOperationException in Aggregate
.Aggregate((f1, f2) => f1 | f2);
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
LoadSettings(); LoadSettings();
@@ -37,6 +48,9 @@ namespace Barotrauma.Networking
//outMsg.WritePadBits(); //outMsg.WritePadBits();
//outMsg.Write((UInt16)QueryPort); //outMsg.Write((UInt16)QueryPort);
NetFlags requiredFlags = GetRequiredFlags(c);
if (!requiredFlags.HasFlag(NetFlags.Properties)) { return; }
WriteNetProperties(outMsg); WriteNetProperties(outMsg);
WriteMonsterEnabled(outMsg); WriteMonsterEnabled(outMsg);
BanList.ServerAdminWrite(outMsg, c); BanList.ServerAdminWrite(outMsg, c);
@@ -45,8 +59,18 @@ namespace Barotrauma.Networking
public void ServerWrite(IWriteMessage outMsg, Client c) public void ServerWrite(IWriteMessage outMsg, Client c)
{ {
outMsg.Write(ServerName); NetFlags requiredFlags = GetRequiredFlags(c);
outMsg.Write(ServerMessageText); outMsg.Write((byte)requiredFlags);
if (requiredFlags.HasFlag(NetFlags.Name))
{
outMsg.Write(ServerName);
}
if (requiredFlags.HasFlag(NetFlags.Message))
{
outMsg.Write(ServerMessageText);
}
outMsg.Write((byte)PlayStyle);
outMsg.Write((byte)MaxPlayers); outMsg.Write((byte)MaxPlayers);
outMsg.Write(HasPassword); outMsg.Write(HasPassword);
outMsg.Write(IsPublic); outMsg.Write(IsPublic);
@@ -54,12 +78,13 @@ namespace Barotrauma.Networking
outMsg.WritePadBits(); outMsg.WritePadBits();
outMsg.WriteRangedInteger(TickRate, 1, 60); outMsg.WriteRangedInteger(TickRate, 1, 60);
WriteExtraCargo(outMsg); if (requiredFlags.HasFlag(NetFlags.Properties))
{
WriteExtraCargo(outMsg);
}
WriteHiddenSubs(outMsg); WriteHiddenSubs(outMsg);
Voting.ServerWrite(outMsg);
if (c.HasPermission(Networking.ClientPermissions.ManageSettings)) if (c.HasPermission(Networking.ClientPermissions.ManageSettings))
{ {
outMsg.Write(true); outMsg.Write(true);
@@ -85,20 +110,20 @@ namespace Barotrauma.Networking
if (flags.HasFlag(NetFlags.Name)) if (flags.HasFlag(NetFlags.Name))
{ {
string serverName = incMsg.ReadString(); string serverName = incMsg.ReadString();
if (ServerName != serverName) changed = true; if (ServerName != serverName) { changed = true; }
ServerName = serverName; ServerName = serverName;
} }
if (flags.HasFlag(NetFlags.Message)) if (flags.HasFlag(NetFlags.Message))
{ {
string serverMessageText = incMsg.ReadString(); string serverMessageText = incMsg.ReadString();
if (ServerMessageText != serverMessageText) changed = true; if (ServerMessageText != serverMessageText) { changed = true; }
ServerMessageText = serverMessageText; ServerMessageText = serverMessageText;
} }
if (flags.HasFlag(NetFlags.Properties)) if (flags.HasFlag(NetFlags.Properties))
{ {
changed |= ReadExtraCargo(incMsg); bool propertiesChanged = ReadExtraCargo(incMsg);
UInt32 count = incMsg.ReadUInt32(); UInt32 count = incMsg.ReadUInt32();
@@ -114,7 +139,7 @@ namespace Barotrauma.Networking
{ {
GameServer.Log(GameServer.ClientLogName(c) + " changed " + netProperties[key].Name + " to " + netProperties[key].Value.ToString(), ServerLog.MessageType.ServerMessage); GameServer.Log(GameServer.ClientLogName(c) + " changed " + netProperties[key].Name + " to " + netProperties[key].Value.ToString(), ServerLog.MessageType.ServerMessage);
} }
changed = true; propertiesChanged = true;
} }
else else
{ {
@@ -124,10 +149,13 @@ namespace Barotrauma.Networking
} }
bool changedMonsterSettings = incMsg.ReadBoolean(); incMsg.ReadPadBits(); bool changedMonsterSettings = incMsg.ReadBoolean(); incMsg.ReadPadBits();
changed |= changedMonsterSettings; propertiesChanged |= changedMonsterSettings;
if (changedMonsterSettings) ReadMonsterEnabled(incMsg); if (changedMonsterSettings) { ReadMonsterEnabled(incMsg); }
changed |= BanList.ServerAdminRead(incMsg, c); propertiesChanged |= BanList.ServerAdminRead(incMsg, c);
changed |= Whitelist.ServerAdminRead(incMsg, c); propertiesChanged |= Whitelist.ServerAdminRead(incMsg, c);
if (propertiesChanged) { UpdateFlag(NetFlags.Properties); }
changed |= propertiesChanged;
} }
if (flags.HasFlag(NetFlags.HiddenSubs)) if (flags.HasFlag(NetFlags.HiddenSubs))
@@ -175,12 +203,14 @@ namespace Barotrauma.Networking
MaxMissionCount = MathHelper.Clamp(maxMissionCount, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit); MaxMissionCount = MathHelper.Clamp(maxMissionCount, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit);
changed |= true; changed |= true;
UpdateFlag(NetFlags.Misc);
} }
if (flags.HasFlag(NetFlags.LevelSeed)) if (flags.HasFlag(NetFlags.LevelSeed))
{ {
GameMain.NetLobbyScreen.LevelSeed = incMsg.ReadString(); GameMain.NetLobbyScreen.LevelSeed = incMsg.ReadString();
changed |= true; changed |= true;
UpdateFlag(NetFlags.LevelSeed);
} }
if (changed) if (changed)
@@ -271,6 +301,8 @@ namespace Barotrauma.Networking
HiddenSubs.UnionWith(doc.Root.GetAttributeStringArray("HiddenSubs", Array.Empty<string>())); HiddenSubs.UnionWith(doc.Root.GetAttributeStringArray("HiddenSubs", Array.Empty<string>()));
SelectedSubmarine = SelectNonHiddenSubmarine(SelectedSubmarine);
string[] defaultAllowedClientNameChars = string[] defaultAllowedClientNameChars =
new string[] { new string[] {
"32-33", "32-33",
@@ -345,7 +377,6 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.SetBotSpawnMode(BotSpawnMode); GameMain.NetLobbyScreen.SetBotSpawnMode(BotSpawnMode);
GameMain.NetLobbyScreen.SetBotCount(BotCount); GameMain.NetLobbyScreen.SetBotCount(BotCount);
GameMain.NetLobbyScreen.SetMaxMissionCount(MaxMissionCount);
List<string> monsterNames = CharacterPrefab.Prefabs.Select(p => p.Identifier).ToList(); List<string> monsterNames = CharacterPrefab.Prefabs.Select(p => p.Identifier).ToList();
MonsterEnabled = new Dictionary<string, bool>(); MonsterEnabled = new Dictionary<string, bool>();
@@ -355,16 +386,25 @@ namespace Barotrauma.Networking
} }
} }
public void SelectNonHiddenSubmarine() public string SelectNonHiddenSubmarine(string current = null)
{ {
if (HiddenSubs.Contains(GameMain.NetLobbyScreen.SelectedSub.Name)) current ??= GameMain.NetLobbyScreen.SelectedSub.Name;
if (HiddenSubs.Contains(current))
{ {
var candidates = GameMain.NetLobbyScreen.GetSubList().Where(s => !HiddenSubs.Contains(s.Name)).ToArray(); var candidates
= GameMain.NetLobbyScreen.GetSubList().Where(s => !HiddenSubs.Contains(s.Name)).ToArray();
if (candidates.Any()) if (candidates.Any())
{ {
GameMain.NetLobbyScreen.SelectedSub = candidates.GetRandom(Rand.RandSync.Unsynced); GameMain.NetLobbyScreen.SelectedSub = candidates.GetRandom(Rand.RandSync.Unsynced);
return GameMain.NetLobbyScreen.SelectedSub.Name;
}
else
{
HiddenSubs.Remove(current);
return current;
} }
} }
return current;
} }
public void LoadClientPermissions() public void LoadClientPermissions()

View File

@@ -69,6 +69,8 @@ namespace Barotrauma.Networking
{ {
GameServer.Log("Saving whitelist", ServerLog.MessageType.ServerMessage); GameServer.Log("Saving whitelist", ServerLog.MessageType.ServerMessage);
GameMain.Server?.ServerSettings?.UpdateFlag(ServerSettings.NetFlags.Properties);
List<string> lines = new List<string>(); List<string> lines = new List<string>();
if (Enabled) if (Enabled)

View File

@@ -1,13 +1,13 @@
#region Using Statements #region Using Statements
using Barotrauma.Steam; using Barotrauma.Steam;
using GameAnalyticsSDK.Net;
using System; using System;
using Barotrauma.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; #if LINUX
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#endif
#endregion #endregion
@@ -56,7 +56,7 @@ namespace Barotrauma
Game = new GameMain(args); Game = new GameMain(args);
Game.Run(); Game.Run();
if (GameSettings.SendUserStatistics) { GameAnalytics.OnQuit(); } if (GameAnalyticsManager.SendUserStatistics) { GameAnalyticsManager.ShutDown(); }
SteamManager.ShutDown(); SteamManager.ShutDown();
} }
@@ -152,6 +152,13 @@ namespace Barotrauma
} }
} }
if (GameAnalyticsManager.SendUserStatistics)
{
//send crash report before appending debug console messages (which may contain non-anonymous information)
GameAnalyticsManager.AddErrorEvent(GameAnalyticsManager.ErrorSeverity.Critical, sb.ToString());
GameAnalyticsManager.ShutDown();
}
sb.AppendLine("Last debug messages:"); sb.AppendLine("Last debug messages:");
DebugConsole.Clear(); DebugConsole.Clear();
for (int i = DebugConsole.Messages.Count - 1; i > 0 && i > DebugConsole.Messages.Count - 15; i--) for (int i = DebugConsole.Messages.Count - 1; i > 0 && i > DebugConsole.Messages.Count - 15; i--)
@@ -171,10 +178,8 @@ namespace Barotrauma
if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); } if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); }
if (GameSettings.SendUserStatistics) if (GameAnalyticsManager.SendUserStatistics)
{ {
GameAnalytics.AddErrorEvent(EGAErrorSeverity.Critical, crashReport);
GameAnalytics.OnQuit();
Console.Write("A crash report (\"servercrashreport.log\") was saved in the root folder of the game and sent to the developers."); Console.Write("A crash report (\"servercrashreport.log\") was saved in the root folder of the game and sent to the developers.");
} }
else else

View File

@@ -51,42 +51,6 @@ namespace Barotrauma
private List<SubmarineInfo> campaignSubmarines; private List<SubmarineInfo> campaignSubmarines;
public void AddCampaignSubmarine(SubmarineInfo sub)
{
if (!campaignSubmarines.Contains(sub))
{
campaignSubmarines.Add(sub);
}
else
{
return;
}
lastUpdateID++;
if (GameMain.NetworkMember?.ServerSettings != null)
{
GameMain.NetworkMember.ServerSettings.ServerDetailsChanged = true;
}
}
public void RemoveCampaignSubmarine(SubmarineInfo sub)
{
if (campaignSubmarines.Contains(sub))
{
campaignSubmarines.Remove(sub);
}
else
{
return;
}
lastUpdateID++;
if (GameMain.NetworkMember?.ServerSettings != null)
{
GameMain.NetworkMember.ServerSettings.ServerDetailsChanged = true;
}
}
public GameModePreset[] GameModes { get; } public GameModePreset[] GameModes { get; }
private int selectedModeIndex; private int selectedModeIndex;
@@ -212,10 +176,7 @@ namespace Barotrauma
} }
private List<SubmarineInfo> subs; private List<SubmarineInfo> subs;
public List<SubmarineInfo> GetSubList() public IReadOnlyList<SubmarineInfo> GetSubList() => subs;
{
return subs;
}
public string LevelSeed public string LevelSeed
{ {

View File

@@ -1,149 +1,152 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.15.15.0</Version> <Version>0.15.17.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon> <ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
<Configurations>Debug;Release;Unstable</Configurations> <Configurations>Debug;Release;Unstable</Configurations>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants> <DefineConstants>DEBUG;TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DefineConstants>TRACE;DEBUG;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;DEBUG;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'">
<DefineConstants>TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<Optimize>true</Optimize> <Optimize>true</Optimize>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'">
<DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<Optimize>true</Optimize> <Optimize>true</Optimize>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.props" />
<Compile Remove="..\BarotraumaShared\SharedSource\Networking\Primitives\Message\WrapperMsg.cs" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
<Content Remove="DedicatedServer.exe" /> <Compile Remove="..\BarotraumaShared\SharedSource\Networking\Primitives\Message\WrapperMsg.cs" />
</ItemGroup> <Content Remove="DedicatedServer.exe" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'!='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Release" /> <ItemGroup Condition="'$(Configuration)'!='Debug'">
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> </ItemGroup>
</ItemGroup>
<ItemGroup>
<!-- Sourced from https://stackoverflow.com/a/45248069 --> <PackageReference Include="RestSharp" Version="106.13.0" />
<Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''"> </ItemGroup>
<PropertyGroup>
<!-- temp file for the git version (lives in "obj" folder)--> <!-- Sourced from https://stackoverflow.com/a/45248069 -->
<VerFile>$(IntermediateOutputPath)gitver</VerFile> <Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''">
<BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile> <PropertyGroup>
</PropertyGroup> <!-- temp file for the git version (lives in "obj" folder)-->
<VerFile>$(IntermediateOutputPath)gitver</VerFile>
<!-- write the hash to the temp file.--> <BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile>
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true"> </PropertyGroup>
<Output TaskParameter="exitcode" ItemName="exitcodes" />
</Exec> <!-- write the hash to the temp file.-->
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" /> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true">
<Output TaskParameter="exitcode" ItemName="exitcodes" />
<Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" /> </Exec>
<Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" /> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" />
<!-- read the version into the GitVersion itemGroup--> <Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<ReadLinesFromFile File="$(VerFile)"> <Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<Output TaskParameter="Lines" ItemName="GitVersion" />
</ReadLinesFromFile> <!-- read the version into the GitVersion itemGroup-->
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <ReadLinesFromFile File="$(VerFile)">
<PropertyGroup> <Output TaskParameter="Lines" ItemName="GitVersion" />
<BuildHash>@(GitVersion)</BuildHash> </ReadLinesFromFile>
</PropertyGroup> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
<PropertyGroup>
<!-- read the branch into the GitBranch itemGroup--> <BuildHash>@(GitVersion)</BuildHash>
<ReadLinesFromFile File="$(BranchFile)"> </PropertyGroup>
<Output TaskParameter="Lines" ItemName="GitBranch" />
</ReadLinesFromFile> <!-- read the branch into the GitBranch itemGroup-->
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <ReadLinesFromFile File="$(BranchFile)">
<PropertyGroup> <Output TaskParameter="Lines" ItemName="GitBranch" />
<BuildBranch>@(GitBranch)</BuildBranch> </ReadLinesFromFile>
</PropertyGroup> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
</Target> <PropertyGroup>
<BuildBranch>@(GitBranch)</BuildBranch>
<Target Name="WriteGitRevision" BeforeTargets="CoreCompile"> </PropertyGroup>
<!-- names the obj/.../CustomAssemblyInfo.cs file --> </Target>
<PropertyGroup>
<CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile> <Target Name="WriteGitRevision" BeforeTargets="CoreCompile">
</PropertyGroup> <!-- names the obj/.../CustomAssemblyInfo.cs file -->
<!-- includes the CustomAssemblyInfo for compilation into your project --> <PropertyGroup>
<ItemGroup> <CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile>
<Compile Include="$(CustomAssemblyInfoFile)" /> </PropertyGroup>
</ItemGroup> <!-- includes the CustomAssemblyInfo for compilation into your project -->
<!-- defines the AssemblyMetadata attribute that will be written --> <ItemGroup>
<ItemGroup> <Compile Include="$(CustomAssemblyInfoFile)" />
<AssemblyAttributes Include="AssemblyMetadata"> </ItemGroup>
<_Parameter1>GitRevision</_Parameter1> <!-- defines the AssemblyMetadata attribute that will be written -->
<_Parameter2>$(BuildHash)</_Parameter2> <ItemGroup>
</AssemblyAttributes> <AssemblyAttributes Include="AssemblyMetadata">
<AssemblyAttributes Include="AssemblyMetadata"> <_Parameter1>GitRevision</_Parameter1>
<_Parameter1>GitBranch</_Parameter1> <_Parameter2>$(BuildHash)</_Parameter2>
<_Parameter2>$(BuildBranch)</_Parameter2> </AssemblyAttributes>
</AssemblyAttributes> <AssemblyAttributes Include="AssemblyMetadata">
<AssemblyAttributes Include="AssemblyMetadata"> <_Parameter1>GitBranch</_Parameter1>
<_Parameter1>ProjectDir</_Parameter1> <_Parameter2>$(BuildBranch)</_Parameter2>
<_Parameter2>$(ProjectDir)</_Parameter2> </AssemblyAttributes>
</AssemblyAttributes> <AssemblyAttributes Include="AssemblyMetadata">
</ItemGroup> <_Parameter1>ProjectDir</_Parameter1>
<!-- writes the attribute to the customAssemblyInfo file --> <_Parameter2>$(ProjectDir)</_Parameter2>
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" /> </AssemblyAttributes>
</Target> </ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file -->
</Project> <WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
</Target>
</Project>

View File

@@ -135,6 +135,7 @@
<Submarine file="Submarines/Kastrull.sub" /> <Submarine file="Submarines/Kastrull.sub" />
<Submarine file="Submarines/KastrullDrone.sub" /> <Submarine file="Submarines/KastrullDrone.sub" />
<Submarine file="Submarines/Orca.sub" /> <Submarine file="Submarines/Orca.sub" />
<Submarine file="Submarines/Orca2.sub" />
<Submarine file="Submarines/Remora.sub" /> <Submarine file="Submarines/Remora.sub" />
<Submarine file="Submarines/RemoraDrone.sub" /> <Submarine file="Submarines/RemoraDrone.sub" />
<Submarine file="Submarines/Selkie.sub" /> <Submarine file="Submarines/Selkie.sub" />

View File

@@ -0,0 +1,18 @@
<Project>
<PropertyGroup Condition="'$(Configuration)'!='Debug'">
<IsDebug>false</IsDebug>
<IsNotDebug>true</IsNotDebug>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<IsDebug>true</IsDebug>
<IsNotDebug>false</IsNotDebug>
</PropertyGroup>
<Target Name="DeployGameAnalyticsBuild" AfterTargets="CoreCompile" >
<Exec Command="dotnet publish $(ProjectDir)../../Libraries/GameAnalytics/GA_SDK_NETSTANDARD/GA_SDK_NETSTANDARD.csproj -c Release -r $(GARuntime) --no-self-contained --nologo -o $(ProjectDir)$(OutputPath)" ContinueOnError="$(IsDebug)" />
</Target>
<Target Name="DeployGameAnalyticsPublish" AfterTargets="PrepareForPublish" >
<Exec Command="dotnet publish $(ProjectDir)../../Libraries/GameAnalytics/GA_SDK_NETSTANDARD/GA_SDK_NETSTANDARD.csproj -c Release -r $(GARuntime) --no-self-contained --nologo -o $(ProjectDir)$(PublishDir)" ContinueOnError="$(IsDebug)" />
</Target>
</Project>

View File

@@ -1,8 +1,6 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using NLog.Targets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {

View File

@@ -1,5 +1,4 @@
using Barotrauma.Extensions; using Barotrauma.Items.Components;
using Barotrauma.Items.Components;
using Barotrauma.Networking; using Barotrauma.Networking;
using FarseerPhysics; using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
@@ -125,6 +124,8 @@ namespace Barotrauma
minGapSize = ConvertUnits.ToDisplayUnits(Math.Min(colliderWidth, colliderLength)); minGapSize = ConvertUnits.ToDisplayUnits(Math.Min(colliderWidth, colliderLength));
} }
public virtual void OnHealed(Character healer, float healAmount) { }
public virtual void OnAttacked(Character attacker, AttackResult attackResult) { } public virtual void OnAttacked(Character attacker, AttackResult attackResult) { }
public virtual void SelectTarget(AITarget target) { } public virtual void SelectTarget(AITarget target) { }
@@ -306,14 +307,15 @@ namespace Barotrauma
public void UnequipEmptyItems(Item parentItem, bool avoidDroppingInSea = true) => UnequipEmptyItems(Character, parentItem, avoidDroppingInSea); public void UnequipEmptyItems(Item parentItem, bool avoidDroppingInSea = true) => UnequipEmptyItems(Character, parentItem, avoidDroppingInSea);
public void UnequipContainedItems(Item parentItem, Func<Item, bool> predicate = null, bool avoidDroppingInSea = true) => UnequipContainedItems(Character, parentItem, predicate, avoidDroppingInSea); public void UnequipContainedItems(Item parentItem, Func<Item, bool> predicate = null, bool avoidDroppingInSea = true, int? unequipMax = null) => UnequipContainedItems(Character, parentItem, predicate, avoidDroppingInSea, unequipMax);
public static void UnequipEmptyItems(Character character, Item parentItem, bool avoidDroppingInSea = true) => UnequipContainedItems(character, parentItem, it => it.Condition <= 0, avoidDroppingInSea); public static void UnequipEmptyItems(Character character, Item parentItem, bool avoidDroppingInSea = true) => UnequipContainedItems(character, parentItem, it => it.Condition <= 0, avoidDroppingInSea);
public static void UnequipContainedItems(Character character, Item parentItem, Func<Item, bool> predicate, bool avoidDroppingInSea = true) public static void UnequipContainedItems(Character character, Item parentItem, Func<Item, bool> predicate, bool avoidDroppingInSea = true, int? unequipMax = null)
{ {
var inventory = parentItem.OwnInventory; var inventory = parentItem.OwnInventory;
if (inventory == null) { return; } if (inventory == null) { return; }
int removed = 0;
if (predicate == null || inventory.AllItems.Any(predicate)) if (predicate == null || inventory.AllItems.Any(predicate))
{ {
foreach (Item containedItem in inventory.AllItemsMod) foreach (Item containedItem in inventory.AllItemsMod)
@@ -326,10 +328,12 @@ namespace Barotrauma
// If we are not inside a friendly sub (= same team), try to put the item in the inventory instead dropping it. // If we are not inside a friendly sub (= same team), try to put the item in the inventory instead dropping it.
if (character.Inventory.TryPutItem(containedItem, character, CharacterInventory.anySlot)) if (character.Inventory.TryPutItem(containedItem, character, CharacterInventory.anySlot))
{ {
if (unequipMax.HasValue && ++removed >= unequipMax) { return; }
continue; continue;
} }
} }
containedItem.Drop(character); containedItem.Drop(character);
if (unequipMax.HasValue && ++removed >= unequipMax) { return; }
} }
} }
} }

View File

@@ -76,7 +76,7 @@ namespace Barotrauma
{ {
string errorMsg = "Invalid AITarget sector direction (" + value + ")\n" + Environment.StackTrace.CleanupStackTrace(); string errorMsg = "Invalid AITarget sector direction (" + value + ")\n" + Environment.StackTrace.CleanupStackTrace();
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("AITarget.SectorDir:" + entity?.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("AITarget.SectorDir:" + entity?.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return; return;
} }
sectorDir = value; sectorDir = value;
@@ -125,7 +125,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace()); DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved", GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
"Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace()); "Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
return Vector2.Zero; return Vector2.Zero;
} }
@@ -144,7 +144,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace()); DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved", GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, GameAnalyticsManager.ErrorSeverity.Error,
"Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace()); "Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
return Vector2.Zero; return Vector2.Zero;
} }

View File

@@ -339,11 +339,15 @@ namespace Barotrauma
{ {
targetingTag = "dead"; targetingTag = "dead";
} }
else if (AIParams.TryGetTarget(targetCharacter.CharacterHealth.GetActiveAfflictionTags(), out CharacterParams.TargetParams tp) && tp.Threshold > Character.GetDamageDoneByAttacker(targetCharacter))
{
targetingTag = tp.Tag;
}
else if (PetBehavior != null && aiTarget.Entity == PetBehavior.Owner) else if (PetBehavior != null && aiTarget.Entity == PetBehavior.Owner)
{ {
targetingTag = "owner"; targetingTag = "owner";
} }
else if (AIParams.TryGetTarget(targetCharacter.SpeciesName, out CharacterParams.TargetParams tP)) else if (AIParams.TryGetTarget(targetCharacter, out CharacterParams.TargetParams tP))
{ {
targetingTag = tP.Tag; targetingTag = tP.Tag;
} }
@@ -353,7 +357,7 @@ namespace Barotrauma
{ {
targetingTag = "husk"; targetingTag = "husk";
} }
else else if (!Character.IsFriendly(targetCharacter))
{ {
if (enemy.CombatStrength > CombatStrength) if (enemy.CombatStrength > CombatStrength)
{ {
@@ -386,6 +390,10 @@ namespace Barotrauma
{ {
targetingTag = "sonar"; targetingTag = "sonar";
} }
if (targetItem.GetComponent<Door>() != null)
{
targetingTag = "door";
}
} }
} }
else if (aiTarget.Entity is Structure) else if (aiTarget.Entity is Structure)
@@ -511,8 +519,7 @@ namespace Barotrauma
} }
else if (avoidTimer <= 0 || activeTriggers.Any() && returnTimer <= 0) else if (avoidTimer <= 0 || activeTriggers.Any() && returnTimer <= 0)
{ {
CharacterParams.TargetParams targetingParams = null; UpdateTargets(out CharacterParams.TargetParams targetingParams);
UpdateTargets(Character, out targetingParams);
updateTargetsTimer = updateTargetsInterval * Rand.Range(0.75f, 1.25f); updateTargetsTimer = updateTargetsInterval * Rand.Range(0.75f, 1.25f);
if (SelectedAiTarget == null) if (SelectedAiTarget == null)
{ {
@@ -1973,7 +1980,7 @@ namespace Barotrauma
ChangeTargetState(attacker, canAttack ? AIState.Attack : AIState.Escape, 100); ChangeTargetState(attacker, canAttack ? AIState.Attack : AIState.Escape, 100);
} }
} }
else if (canAttack && attacker.IsHuman && AIParams.TryGetTarget(attacker.SpeciesName, out CharacterParams.TargetParams targetingParams)) else if (canAttack && attacker.IsHuman && AIParams.TryGetTarget(attacker, out CharacterParams.TargetParams targetingParams))
{ {
if (targetingParams.State == AIState.Aggressive || targetingParams.State == AIState.PassiveAggressive) if (targetingParams.State == AIState.Aggressive || targetingParams.State == AIState.PassiveAggressive)
{ {
@@ -2362,7 +2369,7 @@ namespace Barotrauma
//goes through all the AItargets, evaluates how preferable it is to attack the target, //goes through all the AItargets, evaluates how preferable it is to attack the target,
//whether the Character can see/hear the target and chooses the most preferable target within //whether the Character can see/hear the target and chooses the most preferable target within
//sight/hearing range //sight/hearing range
public AITarget UpdateTargets(Character character, out CharacterParams.TargetParams targetingParams) public AITarget UpdateTargets(out CharacterParams.TargetParams targetingParams)
{ {
AITarget newTarget = null; AITarget newTarget = null;
targetValue = 0; targetValue = 0;
@@ -2386,70 +2393,35 @@ namespace Barotrauma
} }
Character targetCharacter = aiTarget.Entity as Character; Character targetCharacter = aiTarget.Entity as Character;
//ignore the aitarget if it is the Character itself //ignore the aitarget if it is the Character itself
if (targetCharacter == character) { continue; } if (targetCharacter == Character) { continue; }
float valueModifier = 1; float valueModifier = 1;
string targetingTag = null; string targetingTag = GetTargetingTag(aiTarget);
if (targetCharacter != null) if (targetCharacter != null)
{ {
// ignore if target is tagged to be explicitly ignored (Feign Death) // ignore if target is tagged to be explicitly ignored (Feign Death)
if (targetCharacter.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { continue; } if (targetCharacter.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { continue; }
if (AIParams.Targets.None() && Character.IsFriendly(targetCharacter))
if (targetCharacter.IsDead)
{ {
targetingTag = "dead"; continue;
} }
else if (PetBehavior != null && aiTarget.Entity == PetBehavior.Owner) if (targetCharacter.AIController is EnemyAIController enemy)
{ {
targetingTag = "owner"; if (targetingTag == "stronger" && (State == AIState.Avoid || State == AIState.Escape || State == AIState.Flee))
}
else if (AIParams.TryGetTarget(targetCharacter.SpeciesName, out CharacterParams.TargetParams tP))
{
targetingTag = tP.Tag;
}
else
{
if (Character.IsFriendly(targetCharacter))
{ {
continue; if (SelectedAiTarget == aiTarget)
}
if (targetCharacter.AIController is EnemyAIController enemy)
{
if (targetCharacter.IsHusk && AIParams.HasTag("husk"))
{ {
targetingTag = "husk"; // Freightened -> hold on to the target
valueModifier *= 2;
} }
else if (IsBeingChasedBy(targetCharacter))
{ {
if (enemy.CombatStrength > CombatStrength) valueModifier *= 2;
{ }
targetingTag = "stronger"; if (Character.CurrentHull != null && !VisibleHulls.Contains(targetCharacter.CurrentHull))
} {
else if (enemy.CombatStrength < CombatStrength) // Inside but in a different room
{ valueModifier /= 2;
targetingTag = "weaker";
}
else
{
targetingTag = "equal";
}
if (targetingTag == "stronger" && (State == AIState.Avoid || State == AIState.Escape || State == AIState.Flee))
{
if (SelectedAiTarget == aiTarget)
{
// Freightened -> hold on to the target
valueModifier *= 2;
}
if (IsBeingChasedBy(targetCharacter))
{
valueModifier *= 2;
}
if (Character.CurrentHull != null && !VisibleHulls.Contains(targetCharacter.CurrentHull))
{
// Inside but in a different room
valueModifier /= 2;
}
}
} }
} }
} }
@@ -2469,7 +2441,7 @@ namespace Barotrauma
if (aiTarget.Entity is Hull hull) if (aiTarget.Entity is Hull hull)
{ {
// Ignore the target if it's a room and the character is already inside a sub // Ignore the target if it's a room and the character is already inside a sub
if (character.CurrentHull != null) { continue; } if (Character.CurrentHull != null) { continue; }
// Ignore ruins // Ignore ruins
if (hull.Submarine == null) { continue; } if (hull.Submarine == null) { continue; }
if (hull.Submarine.Info.IsRuin) { continue; } if (hull.Submarine.Info.IsRuin) { continue; }
@@ -2479,7 +2451,7 @@ namespace Barotrauma
if (aiTarget.Entity is Item item) if (aiTarget.Entity is Item item)
{ {
door = item.GetComponent<Door>(); door = item.GetComponent<Door>();
bool targetingFromOutsideToInside = item.CurrentHull != null && character.CurrentHull == null; bool targetingFromOutsideToInside = item.CurrentHull != null && Character.CurrentHull == null;
if (targetingFromOutsideToInside) if (targetingFromOutsideToInside)
{ {
if (door != null && (!canAttackDoors && !AIParams.CanOpenDoors) || !canAttackWalls) if (door != null && (!canAttackDoors && !AIParams.CanOpenDoors) || !canAttackWalls)
@@ -2488,28 +2460,12 @@ namespace Barotrauma
continue; continue;
} }
} }
foreach (var prio in AIParams.Targets) if (door == null && targetingFromOutsideToInside)
{ {
if (item.HasTag(prio.Tag)) if (item.Submarine?.Info is { IsRuin: true })
{ {
targetingTag = prio.Tag; // Ignore ruin items when the creature is outside.
break; continue;
}
}
if (door == null && targetingTag == null)
{
if (item.GetComponent<Sonar>() != null)
{
targetingTag = "sonar";
}
else if (targetingFromOutsideToInside)
{
targetingTag = "room";
if (item.Submarine?.Info.IsRuin != null)
{
// Ignore ruin items when the creature is outside.
continue;
}
} }
} }
else if (targetingTag == "nasonov") else if (targetingTag == "nasonov")
@@ -2521,14 +2477,13 @@ namespace Barotrauma
} }
} }
// Ignore the target if it's a decoy and the character is already inside a sub // Ignore the target if it's a decoy and the character is already inside a sub
if (character.CurrentHull != null && targetingTag == "decoy") if (Character.CurrentHull != null && targetingTag == "decoy")
{ {
continue; continue;
} }
} }
else if (aiTarget.Entity is Structure s) else if (aiTarget.Entity is Structure s)
{ {
targetingTag = "wall";
if (!s.HasBody) if (!s.HasBody)
{ {
// Ignore structures that doesn't have a body (not walls) // Ignore structures that doesn't have a body (not walls)
@@ -2537,7 +2492,7 @@ namespace Barotrauma
if (s.IsPlatform) { continue; } if (s.IsPlatform) { continue; }
if (s.Submarine == null) { continue; } if (s.Submarine == null) { continue; }
if (s.Submarine.Info.IsRuin) { continue; } if (s.Submarine.Info.IsRuin) { continue; }
bool isCharacterInside = character.CurrentHull != null; bool isCharacterInside = Character.CurrentHull != null;
bool isInnerWall = s.prefab.Tags.Contains("inner"); bool isInnerWall = s.prefab.Tags.Contains("inner");
if (isInnerWall && !isCharacterInside) if (isInnerWall && !isCharacterInside)
{ {
@@ -2624,21 +2579,12 @@ namespace Barotrauma
} }
} }
} }
else
{
targetingTag = "room";
}
if (door != null) if (door != null)
{ {
// If there's not a more specific tag for the door
if (string.IsNullOrEmpty(targetingTag) || targetingTag == "room")
{
targetingTag = "door";
}
if (door.Item.Submarine == null) { continue; } if (door.Item.Submarine == null) { continue; }
bool isOutdoor = door.LinkedGap?.FlowTargetHull != null && !door.LinkedGap.IsRoomToRoom; bool isOutdoor = door.LinkedGap?.FlowTargetHull != null && !door.LinkedGap.IsRoomToRoom;
// Ignore inner doors when outside // Ignore inner doors when outside
if (character.CurrentHull == null && !isOutdoor) { continue; } if (Character.CurrentHull == null && !isOutdoor) { continue; }
bool isOpen = door.CanBeTraversed; bool isOpen = door.CanBeTraversed;
if (!isOpen) if (!isOpen)
{ {
@@ -2651,7 +2597,7 @@ namespace Barotrauma
} }
if (IsAggressiveBoarder) if (IsAggressiveBoarder)
{ {
if (character.CurrentHull == null) if (Character.CurrentHull == null)
{ {
// Increase the priority if the character is outside and the door is from outside to inside // Increase the priority if the character is outside and the door is from outside to inside
if (door.CanBeTraversed) if (door.CanBeTraversed)
@@ -2679,14 +2625,14 @@ namespace Barotrauma
if (targetingTag == null) { continue; } if (targetingTag == null) { continue; }
var targetParams = GetTargetParams(targetingTag); var targetParams = GetTargetParams(targetingTag);
if (targetParams == null) { continue; } if (targetParams == null) { continue; }
if (targetParams.IgnoreInside && character.CurrentHull != null) { continue; } if (targetParams.IgnoreInside && Character.CurrentHull != null) { continue; }
if (targetParams.IgnoreOutside && character.CurrentHull == null) { continue; } if (targetParams.IgnoreOutside && Character.CurrentHull == null) { continue; }
if (targetParams.IgnoreIncapacitated && targetCharacter != null && targetCharacter.IsIncapacitated) { continue; } if (targetParams.IgnoreIncapacitated && targetCharacter != null && targetCharacter.IsIncapacitated) { continue; }
if (targetParams.IgnoreIfNotInSameSub) if (targetParams.IgnoreIfNotInSameSub)
{ {
if (aiTarget.Entity.Submarine != Character.Submarine) { continue; } if (aiTarget.Entity.Submarine != Character.Submarine) { continue; }
var targetHull = targetCharacter != null ? targetCharacter.CurrentHull : aiTarget.Entity is Item it ? it.CurrentHull : null; var targetHull = targetCharacter != null ? targetCharacter.CurrentHull : aiTarget.Entity is Item it ? it.CurrentHull : null;
if ((targetHull == null) != (character.CurrentHull == null)) { continue; } if ((targetHull == null) != (Character.CurrentHull == null)) { continue; }
} }
if (targetParams.State == AIState.Observe || targetParams.State == AIState.Eat) if (targetParams.State == AIState.Observe || targetParams.State == AIState.Eat)
{ {
@@ -2706,7 +2652,7 @@ namespace Barotrauma
{ {
target = selectedTargetingParams == targetParams ? targetParams.ThresholdMax : targetParams.ThresholdMin; target = selectedTargetingParams == targetParams ? targetParams.ThresholdMax : targetParams.ThresholdMin;
} }
if (character.HealthPercentage > target) if (Character.HealthPercentage > target)
{ {
continue; continue;
} }
@@ -2721,7 +2667,7 @@ namespace Barotrauma
// Halve the priority for each swarm mate targeting the same target -> reduces stacking // Halve the priority for each swarm mate targeting the same target -> reduces stacking
foreach (Character otherCharacter in SwarmBehavior.Members) foreach (Character otherCharacter in SwarmBehavior.Members)
{ {
if (otherCharacter == character) { continue; } if (otherCharacter == Character) { continue; }
if (otherCharacter.AIController?.SelectedAiTarget != aiTarget) { continue; } if (otherCharacter.AIController?.SelectedAiTarget != aiTarget) { continue; }
valueModifier /= 2; valueModifier /= 2;
} }
@@ -2731,15 +2677,15 @@ namespace Barotrauma
// The same as above, but using all the friendly characters in the level. // The same as above, but using all the friendly characters in the level.
foreach (Character otherCharacter in Character.CharacterList) foreach (Character otherCharacter in Character.CharacterList)
{ {
if (otherCharacter == character) { continue; } if (otherCharacter == Character) { continue; }
if (otherCharacter.AIController?.SelectedAiTarget != aiTarget) { continue; } if (otherCharacter.AIController?.SelectedAiTarget != aiTarget) { continue; }
if (!character.IsFriendly(otherCharacter)) { continue; } if (!Character.IsFriendly(otherCharacter)) { continue; }
valueModifier /= 2; valueModifier /= 2;
} }
} }
} }
if (!aiTarget.IsWithinSector(WorldPosition)) { continue; } if (!aiTarget.IsWithinSector(WorldPosition)) { continue; }
Vector2 toTarget = aiTarget.WorldPosition - character.WorldPosition; Vector2 toTarget = aiTarget.WorldPosition - Character.WorldPosition;
float dist = toTarget.Length(); float dist = toTarget.Length();
float nonModifiedDist = dist; float nonModifiedDist = dist;
//if the target has been within range earlier, the character will notice it more easily //if the target has been within range earlier, the character will notice it more easily
@@ -2829,7 +2775,7 @@ namespace Barotrauma
Character owner = GetOwner(i); Character owner = GetOwner(i);
// Don't target items that we own. // Don't target items that we own.
// This is a rare case, and almost entirely related to Humanhusks, so let's check it last to reduce unnecessary checks (although the check shouldn't be expensive) // This is a rare case, and almost entirely related to Humanhusks, so let's check it last to reduce unnecessary checks (although the check shouldn't be expensive)
if (owner == character) { continue; } if (owner == Character) { continue; }
if (owner != null && (Character.IsFriendly(owner) || owner.AiTarget != null && ignoredTargets.Contains(owner.AiTarget))) if (owner != null && (Character.IsFriendly(owner) || owner.AiTarget != null && ignoredTargets.Contains(owner.AiTarget)))
{ {
continue; continue;

View File

@@ -85,6 +85,7 @@ namespace Barotrauma
/// List of previous attacks done to this character /// List of previous attacks done to this character
/// </summary> /// </summary>
private readonly Dictionary<Character, AttackResult> previousAttackResults = new Dictionary<Character, AttackResult>(); private readonly Dictionary<Character, AttackResult> previousAttackResults = new Dictionary<Character, AttackResult>();
private readonly Dictionary<Character, float> previousHealAmounts = new Dictionary<Character, float>();
private readonly SteeringManager outsideSteering, insideSteering; private readonly SteeringManager outsideSteering, insideSteering;
@@ -187,6 +188,15 @@ namespace Barotrauma
foreach (var previousAttackResult in previousAttackResults) foreach (var previousAttackResult in previousAttackResults)
{ {
RespondToAttack(previousAttackResult.Key, previousAttackResult.Value); RespondToAttack(previousAttackResult.Key, previousAttackResult.Value);
if (previousHealAmounts.ContainsKey(previousAttackResult.Key))
{
//gradually forget past heals
previousHealAmounts[previousAttackResult.Key] = Math.Min(previousHealAmounts[previousAttackResult.Key] - 5.0f, 100.0f);
if (previousHealAmounts[previousAttackResult.Key] <= 0.0f)
{
previousHealAmounts.Remove(previousAttackResult.Key);
}
}
} }
previousAttackResults.Clear(); previousAttackResults.Clear();
respondToAttackTimer = RespondToAttackInterval; respondToAttackTimer = RespondToAttackInterval;
@@ -237,39 +247,41 @@ namespace Barotrauma
if (Character.Submarine == null) if (Character.Submarine == null)
{ {
// When the character is outside, far enough from the target, and the direct route is blocked, obstacleRaycastTimer -= deltaTime;
// use the indoor steering with the main and side path waypoints to help avoid getting stuck in level walls if (obstacleRaycastTimer <= 0)
if (SelectedAiTarget?.Entity != null && !IsCloseEnoughToTarget(2000, useTargetSub: false))
{ {
obstacleRaycastTimer -= deltaTime; obstacleRaycastTimer = obstacleRaycastIntervalLong;
if (obstacleRaycastTimer <= 0) if (SelectedAiTarget?.Entity == null || SelectedAiTarget.Entity is ISpatialEntity target && target.Submarine == null || !IsCloseEnoughToTarget(2000, useTargetSub: false))
{ {
obstacleRaycastTimer = obstacleRaycastIntervalLong; // If the target is behind a level wall, switch to the pathing to get around the obstacles.
Vector2 rayEnd = SelectedAiTarget.Entity.SimPosition; ISpatialEntity spatialTarget = SelectedAiTarget?.Entity;
if (SelectedAiTarget.Entity.Submarine != null) if (spatialTarget == null)
{ {
rayEnd += SelectedAiTarget.Entity.Submarine.SimPosition; var gotoObjective = ObjectiveManager.GetActiveObjective<AIObjectiveGoTo>();
spatialTarget = gotoObjective?.Target;
} }
IEnumerable<FarseerPhysics.Dynamics.Body> ignoredBodies = null; if (spatialTarget == null)
if (SelectedAiTarget.Entity is ISpatialEntity spatialTarget)
{ {
UseIndoorSteeringOutside = false;
}
else
{
IEnumerable<FarseerPhysics.Dynamics.Body> ignoredBodies = null;
Vector2 rayEnd = spatialTarget.SimPosition;
Submarine targetSub = spatialTarget.Submarine; Submarine targetSub = spatialTarget.Submarine;
if (targetSub != null) if (targetSub != null)
{ {
rayEnd += targetSub.SimPosition;
ignoredBodies = targetSub.PhysicsBody.FarseerBody.ToEnumerable(); ignoredBodies = targetSub.PhysicsBody.FarseerBody.ToEnumerable();
} }
var obstacle = Submarine.PickBody(SimPosition, rayEnd, ignoredBodies, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall);
UseIndoorSteeringOutside = obstacle != null;
} }
var obstacle = Submarine.PickBody(SimPosition, rayEnd, ignoredBodies, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall);
UseIndoorSteeringOutside = obstacle != null;
} }
} else
else
{
UseIndoorSteeringOutside = false;
if (hasValidPath)
{ {
obstacleRaycastTimer -= deltaTime; UseIndoorSteeringOutside = false;
if (obstacleRaycastTimer <= 0) if (hasValidPath)
{ {
obstacleRaycastTimer = obstacleRaycastIntervalShort; obstacleRaycastTimer = obstacleRaycastIntervalShort;
// Swimming outside and using the path finder -> check that the path is not blocked with anything (the path finder doesn't know about other subs). // Swimming outside and using the path finder -> check that the path is not blocked with anything (the path finder doesn't know about other subs).
@@ -332,25 +344,10 @@ namespace Barotrauma
} }
} }
} }
IsInsideCave = Character.CurrentHull == null && Level.Loaded?.Caves.FirstOrDefault(c => c.Area.Contains(Character.WorldPosition)) is Level.Cave;
// Check whether the character is inside a cave if (UseIndoorSteeringOutside || Character.CurrentHull?.Submarine != null || hasValidPath || IsCloseEnoughToTarget(steeringBuffer))
if (IsInsideCave)
{
// If the character was inside a cave, require them to move a bit further from the area to set the field back to false
// This is to avoid any twitchy behavior with the steering managers
IsInsideCave = Character.CurrentHull == null && Level.Loaded?.Caves.FirstOrDefault(c =>
{
var area = c.Area;
area.Inflate(new Vector2(100));
return area.Contains(Character.WorldPosition);
}) is Level.Cave;
}
else
{
IsInsideCave = Character.CurrentHull == null && Level.Loaded?.Caves.FirstOrDefault(c => c.Area.Contains(Character.WorldPosition)) is Level.Cave;
}
if (UseIndoorSteeringOutside || IsInsideCave || Character.CurrentHull?.Submarine != null || hasValidPath || IsCloseEnoughToTarget(steeringBuffer))
{ {
if (steeringManager != insideSteering) if (steeringManager != insideSteering)
{ {
@@ -601,7 +598,7 @@ namespace Barotrauma
takeMaskOff = false; takeMaskOff = false;
break; break;
} }
else if (gotoObjective.mimic) else if (gotoObjective.Mimic)
{ {
if (!removeSuit) if (!removeSuit)
{ {
@@ -880,7 +877,8 @@ namespace Barotrauma
if (target.CurrentHull != hull || !target.Enabled) { continue; } if (target.CurrentHull != hull || !target.Enabled) { continue; }
if (AIObjectiveFightIntruders.IsValidTarget(target, Character)) if (AIObjectiveFightIntruders.IsValidTarget(target, Character))
{ {
if (AddTargets<AIObjectiveFightIntruders, Character>(Character, target) && newOrder == null) bool arrested = AIObjectiveFightIntruders.ShouldArrest(target, Character) && target.HasEquippedItem("handlocker");
if (!arrested && AddTargets<AIObjectiveFightIntruders, Character>(Character, target) && newOrder == null)
{ {
var orderPrefab = Order.GetPrefab("reportintruders"); var orderPrefab = Order.GetPrefab("reportintruders");
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character); newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
@@ -1033,6 +1031,19 @@ namespace Barotrauma
} }
} }
public override void OnHealed(Character healer, float healAmount)
{
if (healer == null || healAmount <= 0.0f) { return; }
if (previousHealAmounts.ContainsKey(healer))
{
previousHealAmounts[healer] += healAmount;
}
else
{
previousHealAmounts.Add(healer, healAmount);
}
}
public override void OnAttacked(Character attacker, AttackResult attackResult) public override void OnAttacked(Character attacker, AttackResult attackResult)
{ {
// The attack incapacitated/killed the character: respond immediately to trigger nearby characters because the update loop no longer runs // The attack incapacitated/killed the character: respond immediately to trigger nearby characters because the update loop no longer runs
@@ -1076,11 +1087,16 @@ namespace Barotrauma
} }
private void RespondToAttack(Character attacker, AttackResult attackResult) private void RespondToAttack(Character attacker, AttackResult attackResult)
{ {
float healAmount = 0.0f;
if (attacker != null)
{
previousHealAmounts.TryGetValue(attacker, out healAmount);
}
// excluding poisons etc // excluding poisons etc
float realDamage = attackResult.Damage; float realDamage = attackResult.Damage - healAmount;
// including poisons etc // including poisons etc
float totalDamage = realDamage; float totalDamage = realDamage - healAmount;
if (attackResult.Afflictions != null) if (attackResult.Afflictions != null)
{ {
foreach (Affliction affliction in attackResult.Afflictions) foreach (Affliction affliction in attackResult.Afflictions)
@@ -1128,7 +1144,7 @@ namespace Barotrauma
// Should not cancel any existing ai objectives (so that if the character attacked you and then helped, we still would want to retaliate). // Should not cancel any existing ai objectives (so that if the character attacked you and then helped, we still would want to retaliate).
return; return;
} }
float cumulativeDamage = GetDamageDoneByAttacker(attacker); float cumulativeDamage = Character.GetDamageDoneByAttacker(attacker);
bool isAccidental = attacker.IsBot && !IsMentallyUnstable && !attacker.AIController.IsMentallyUnstable && Character.CombatAction == null; bool isAccidental = attacker.IsBot && !IsMentallyUnstable && !attacker.AIController.IsMentallyUnstable && Character.CombatAction == null;
if (isAccidental) if (isAccidental)
{ {
@@ -1197,7 +1213,7 @@ namespace Barotrauma
if (Character.Submarine != null && Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine)) if (Character.Submarine != null && Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
{ {
// Non-friendly // Non-friendly
InformOtherNPCs(GetDamageDoneByAttacker(attacker)); InformOtherNPCs(Character.GetDamageDoneByAttacker(attacker));
} }
if (Character.IsBot) if (Character.IsBot)
{ {
@@ -1601,21 +1617,19 @@ namespace Barotrauma
} }
} }
public static void ItemTaken(Item item, Character character) public static void ItemTaken(Item item, Character thief)
{ {
if (item == null || character == null || item.GetComponent<LevelResource>() != null) { return; } if (item == null || thief == null || item.GetComponent<LevelResource>() != null) { return; }
Character thief = character;
bool someoneSpoke = false;
bool someoneSpoke = false;
bool stolenItemsInside = item.OwnInventory?.FindAllItems(it => it.SpawnedInCurrentOutpost && !it.AllowStealing, recursive: true).Any() ?? false; bool stolenItemsInside = item.OwnInventory?.FindAllItems(it => it.SpawnedInCurrentOutpost && !it.AllowStealing, recursive: true).Any() ?? false;
if ((item.SpawnedInCurrentOutpost && !item.AllowStealing || stolenItemsInside) && thief.TeamID != CharacterTeamType.FriendlyNPC && !item.HasTag("handlocker")) if ((item.SpawnedInCurrentOutpost && !item.AllowStealing || stolenItemsInside) && thief.TeamID != CharacterTeamType.FriendlyNPC && !item.HasTag("handlocker"))
{ {
foreach (Character otherCharacter in Character.CharacterList) foreach (Character otherCharacter in Character.CharacterList)
{ {
if (otherCharacter == thief || otherCharacter.TeamID == thief.TeamID || otherCharacter.IsDead || if (otherCharacter == thief || otherCharacter.TeamID == thief.TeamID || otherCharacter.IsIncapacitated || otherCharacter.Stun > 0.0f ||
otherCharacter.Info?.Job == null || otherCharacter.Info?.Job == null || !(otherCharacter.AIController is HumanAIController otherHumanAI) ||
!(otherCharacter.AIController is HumanAIController otherHumanAI) ||
!otherHumanAI.VisibleHulls.Contains(thief.CurrentHull)) !otherHumanAI.VisibleHulls.Contains(thief.CurrentHull))
{ {
continue; continue;
@@ -1624,13 +1638,13 @@ namespace Barotrauma
if (!otherCharacter.CanSeeCharacter(thief)) { continue; } if (!otherCharacter.CanSeeCharacter(thief)) { continue; }
// Don't react if the player is taking an extinguisher and there's any fires on the sub, or diving gear when the sub is flooding // Don't react if the player is taking an extinguisher and there's any fires on the sub, or diving gear when the sub is flooding
// -> allow them to use the emergency items // -> allow them to use the emergency items
if (character.Submarine != null) if (thief.Submarine != null)
{ {
var connectedHulls = character.Submarine.GetHulls(alsoFromConnectedSubs: true); var connectedHulls = thief.Submarine.GetHulls(alsoFromConnectedSubs: true);
if (item.HasTag("fireextinguisher") && connectedHulls.Any(h => h.FireSources.Any())) { continue; } if (item.HasTag("fireextinguisher") && connectedHulls.Any(h => h.FireSources.Any())) { continue; }
if (item.HasTag("diving") && connectedHulls.Any(h => h.ConnectedGaps.Any(g => AIObjectiveFixLeaks.IsValidTarget(g, thief)))) { continue; } if (item.HasTag("diving") && connectedHulls.Any(h => h.ConnectedGaps.Any(g => AIObjectiveFixLeaks.IsValidTarget(g, thief)))) { continue; }
} }
if (!someoneSpoke && !character.IsIncapacitated && character.Stun <= 0.0f) if (!someoneSpoke)
{ {
if (!item.StolenDuringRound && GameMain.GameSession?.Campaign?.Map?.CurrentLocation != null) if (!item.StolenDuringRound && GameMain.GameSession?.Campaign?.Map?.CurrentLocation != null)
{ {
@@ -1663,7 +1677,7 @@ namespace Barotrauma
} }
else if (item.OwnInventory?.FindItem(it => it.SpawnedInCurrentOutpost && !item.AllowStealing, true) is { } foundItem) else if (item.OwnInventory?.FindItem(it => it.SpawnedInCurrentOutpost && !item.AllowStealing, true) is { } foundItem)
{ {
ItemTaken(foundItem, character); ItemTaken(foundItem, thief);
} }
bool TriggerSecurity(HumanAIController humanAI) bool TriggerSecurity(HumanAIController humanAI)
@@ -1791,17 +1805,6 @@ namespace Barotrauma
humanAI.ObjectiveManager.GetObjective<T1>()?.ReportedTargets.Remove(target)); humanAI.ObjectiveManager.GetObjective<T1>()?.ReportedTargets.Remove(target));
} }
public float GetDamageDoneByAttacker(Character otherCharacter)
{
float dmg = 0;
Character.Attacker attacker = Character.LastAttackers.LastOrDefault(a => a.Character == otherCharacter);
if (attacker != null)
{
dmg = attacker.Damage;
}
return dmg;
}
private void StoreHullSafety(Hull hull, HullSafety safety) private void StoreHullSafety(Hull hull, HullSafety safety)
{ {
if (knownHulls.ContainsKey(hull)) if (knownHulls.ContainsKey(hull))

View File

@@ -26,10 +26,9 @@ namespace Barotrauma
private float findPathTimer; private float findPathTimer;
private float checkDoorsTimer;
private float buttonPressCooldown; private float buttonPressCooldown;
const float ButtonPressInterval = 0.25f;
public SteeringPath CurrentPath public SteeringPath CurrentPath
{ {
get { return currentPath; } get { return currentPath; }
@@ -97,9 +96,10 @@ namespace Barotrauma
public override void Update(float speed) public override void Update(float speed)
{ {
base.Update(speed); base.Update(speed);
float step = 1.0f / 60.0f;
buttonPressCooldown -= 1.0f / 60.0f; checkDoorsTimer -= step;
findPathTimer -= 1.0f / 60.0f; buttonPressCooldown -= step;
findPathTimer -= step;
} }
public void SetPath(SteeringPath path) public void SetPath(SteeringPath path)
@@ -204,24 +204,23 @@ namespace Barotrauma
pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && character.PressureProtection <= 0; pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && character.PressureProtection <= 0;
var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", minGapSize, startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility); var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", minGapSize, startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility);
bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.Combine()) <= 0; bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.Combine()) <= 0;
if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable) if (!useNewPath && currentPath?.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable)
{ {
// Check if the new path is the same as the old, in which case we just ignore it and continue using the old path (or the progress would reset). // Check if the new path is the same as the old, in which case we just ignore it and continue using the old path (or the progress would reset).
if (IsIdenticalPath()) if (IsIdenticalPath())
{ {
useNewPath = false; useNewPath = false;
} }
else else if (!character.IsClimbing)
{ {
// Use the new path if it has significantly lower cost (don't change the path if it has marginally smaller cost. This reduces navigating backwards due to new path that is calculated from the node just behind us). // Use the new path if it has significantly lower cost (don't change the path if it has marginally smaller cost. This reduces navigating backwards due to new path that is calculated from the node just behind us).
float t = (float)currentPath.CurrentIndex / (currentPath.Nodes.Count - 1); float t = (float)currentPath.CurrentIndex / (currentPath.Nodes.Count - 1);
useNewPath = newPath.Cost < currentPath.Cost * MathHelper.Lerp(0.95f, 0, t); useNewPath = newPath.Cost < currentPath.Cost * MathHelper.Lerp(0.95f, 0, t);
if (!useNewPath && character.Submarine != null && !character.IsClimbing) if (!useNewPath && character.Submarine != null)
{ {
// It's possible that the current path was calculated from a start point that is no longer valid. // It's possible that the current path was calculated from a start point that is no longer valid.
// Therefore, let's accept also paths with a greater cost than the current, if the current node is much farther than the new start node. // Therefore, let's accept also paths with a greater cost than the current, if the current node is much farther than the new start node.
// This is a special case for cases e.g. where the character falls and thus needs a new path. // This is a special case for cases e.g. where the character falls and thus needs a new path.
// Don't do this outside or when climbing ladders, because both cause issues.
useNewPath = Vector2.DistanceSquared(character.WorldPosition, currentPath.CurrentNode.WorldPosition) > Math.Pow(Vector2.Distance(character.WorldPosition, newPath.Nodes.First().WorldPosition) * 3, 2); useNewPath = Vector2.DistanceSquared(character.WorldPosition, currentPath.CurrentNode.WorldPosition) > Math.Pow(Vector2.Distance(character.WorldPosition, newPath.Nodes.First().WorldPosition) * 3, 2);
} }
} }
@@ -315,7 +314,8 @@ namespace Barotrauma
return currentTarget - pos2; return currentTarget - pos2;
} }
bool doorsChecked = false; bool doorsChecked = false;
if (!character.LockHands && buttonPressCooldown <= 0.0f) checkDoorsTimer = Math.Min(checkDoorsTimer, GetDoorCheckTime());
if (!character.LockHands && checkDoorsTimer <= 0.0f)
{ {
CheckDoorsInPath(); CheckDoorsInPath();
doorsChecked = true; doorsChecked = true;
@@ -336,7 +336,7 @@ namespace Barotrauma
{ {
if (character.CanInteractWith(ladders.Item)) if (character.CanInteractWith(ladders.Item))
{ {
ladders.Item.TryInteract(character, false, true); ladders.Item.TryInteract(character, forceSelectKey: true);
} }
else else
{ {
@@ -347,7 +347,7 @@ namespace Barotrauma
if (previousLadders != null && previousLadders != ladders && character.SelectedConstruction != previousLadders.Item && if (previousLadders != null && previousLadders != ladders && character.SelectedConstruction != previousLadders.Item &&
character.CanInteractWith(previousLadders.Item) && Math.Abs(previousLadders.Item.WorldPosition.X - ladders.Item.WorldPosition.X) < 5) character.CanInteractWith(previousLadders.Item) && Math.Abs(previousLadders.Item.WorldPosition.X - ladders.Item.WorldPosition.X) < 5)
{ {
previousLadders.Item.TryInteract(character, false, true); previousLadders.Item.TryInteract(character, forceSelectKey: true);
} }
} }
} }
@@ -387,7 +387,7 @@ namespace Barotrauma
// Try to change the ladder (hatches between two submarines) // Try to change the ladder (hatches between two submarines)
if (character.SelectedConstruction != nextLadder.Item && nextLadder.Item.IsInsideTrigger(character.WorldPosition)) if (character.SelectedConstruction != nextLadder.Item && nextLadder.Item.IsInsideTrigger(character.WorldPosition))
{ {
nextLadder.Item.TryInteract(character, false, true); nextLadder.Item.TryInteract(character, forceSelectKey: true);
} }
} }
if (isAboveFloor || nextLadderSameAsCurrent) if (isAboveFloor || nextLadderSameAsCurrent)
@@ -487,7 +487,14 @@ namespace Barotrauma
else else
{ {
// We'll want this to run each time, because the delegate is used to find a valid button component. // We'll want this to run each time, because the delegate is used to find a valid button component.
bool canAccessButtons = door.Item.GetConnectedComponents<Controller>(true).Any(b => b.HasAccess(character) && (buttonFilter == null || buttonFilter(b))); bool canAccessButtons = false;
foreach (var button in door.Item.GetConnectedComponents<Controller>(true))
{
if (button.HasAccess(character) && (buttonFilter == null || buttonFilter(button)))
{
canAccessButtons = true;
}
}
return canAccessButtons || door.IsOpen || ShouldBreakDoor(door); return canAccessButtons || door.IsOpen || ShouldBreakDoor(door);
} }
} }
@@ -500,8 +507,22 @@ namespace Barotrauma
return ConvertUnits.ToDisplayUnits(Math.Max(colliderSize.X, colliderSize.Y)); return ConvertUnits.ToDisplayUnits(Math.Max(colliderSize.X, colliderSize.Y));
} }
private (Door door, bool state) lastDoor;
private float GetDoorCheckTime()
{
if (steering.LengthSquared() > 0)
{
return character.AnimController.IsMovingFast ? 0.1f : 0.3f;
}
else
{
return float.PositiveInfinity;
}
}
private void CheckDoorsInPath() private void CheckDoorsInPath()
{ {
checkDoorsTimer = GetDoorCheckTime();
if (!canOpenDoors) { return; } if (!canOpenDoors) { return; }
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
@@ -518,8 +539,7 @@ namespace Barotrauma
} }
else else
{ {
bool closeDoors = character.IsBot && character.IsInFriendlySub || character.Params.AI != null && character.Params.AI.KeepDoorsClosed; if (i == 0)
if (i == 0 || !closeDoors)
{ {
currentWaypoint = currentPath.CurrentNode; currentWaypoint = currentPath.CurrentNode;
nextWaypoint = currentPath.NextNode; nextWaypoint = currentPath.NextNode;
@@ -540,7 +560,7 @@ namespace Barotrauma
if (currentWaypoint.ConnectedDoor.LinkedGap != null) if (currentWaypoint.ConnectedDoor.LinkedGap != null)
{ {
// Keep the airlock doors closed, but not in ruins/wrecks // Keep the airlock doors closed, but not in ruins/wrecks
if (currentWaypoint.ConnectedDoor.LinkedGap.IsRoomToRoom || currentWaypoint.Submarine?.Info.IsRuin != null || currentWaypoint.Submarine?.Info.IsWreck != null) if (currentWaypoint.ConnectedDoor.LinkedGap.IsRoomToRoom && currentWaypoint.CurrentHull is { IsWetRoom: false } || currentWaypoint.Submarine == null || currentWaypoint.Submarine.Info.IsRuin || currentWaypoint.Submarine.Info.IsWreck)
{ {
shouldBeOpen = true; shouldBeOpen = true;
door = currentWaypoint.ConnectedDoor; door = currentWaypoint.ConnectedDoor;
@@ -566,28 +586,49 @@ namespace Barotrauma
} }
if (door == null) { return; } if (door == null) { return; }
if (door.BotsShouldKeepOpen) { shouldBeOpen = true; }
//toggle the door if it's the previous node and open, or if it's current node and closed
if ((door.IsOpen || door.IsBroken) != shouldBeOpen) if ((door.IsOpen || door.IsBroken) != shouldBeOpen)
{ {
if (!shouldBeOpen)
{
if (character.AIController is HumanAIController humanAI)
{
bool keepDoorsClosed = character.IsBot && door.Item.Submarine?.TeamID == character.TeamID || character.Params.AI != null && character.Params.AI.KeepDoorsClosed;
if (!keepDoorsClosed) { return; }
bool isInAirlock = door.Item.CurrentHull is { IsWetRoom: true } || character.CurrentHull is { IsWetRoom: true };
if (!isInAirlock)
{
// Don't slam the door at anyones face
if (Character.CharacterList.Any(c => c != character && humanAI.IsFriendly(c) && humanAI.VisibleHulls.Contains(c.CurrentHull) && !c.IsUnconscious))
{
return;
}
}
}
}
Controller closestButton = null; Controller closestButton = null;
float closestDist = 0; float closestDist = 0;
bool canAccess = CanAccessDoor(door, button => bool canAccess = CanAccessDoor(door, button =>
{ {
if (currentWaypoint == null) { return true; } if (currentWaypoint == null) { return true; }
// Check that the button is on the right side of the door. // Check that the button is on the right side of the door. If the door is open, doesn't matter
if (door.LinkedGap.IsHorizontal) if (!door.IsOpen)
{ {
int dir = Math.Sign((nextWaypoint ?? currentWaypoint).WorldPosition.X - door.Item.WorldPosition.X); if (door.LinkedGap.IsHorizontal)
if (button.Item.WorldPosition.X * dir > door.Item.WorldPosition.X * dir) { return false; } {
} int dir = Math.Sign((nextWaypoint ?? currentWaypoint).WorldPosition.X - door.Item.WorldPosition.X);
else if (button.Item.WorldPosition.X * dir > door.Item.WorldPosition.X * dir) { return false; }
{ }
int dir = Math.Sign((nextWaypoint ?? currentWaypoint).WorldPosition.Y - door.Item.WorldPosition.Y); else
if (button.Item.WorldPosition.Y * dir > door.Item.WorldPosition.Y * dir) { return false; } {
int dir = Math.Sign((nextWaypoint ?? currentWaypoint).WorldPosition.Y - door.Item.WorldPosition.Y);
if (button.Item.WorldPosition.Y * dir > door.Item.WorldPosition.Y * dir) { return false; }
}
} }
float distance = Vector2.DistanceSquared(button.Item.WorldPosition, character.WorldPosition); float distance = Vector2.DistanceSquared(button.Item.WorldPosition, character.WorldPosition);
if (closestButton == null || distance < closestDist) if (closestButton == null || distance < closestDist && character.CanSeeTarget(button.Item))
{ {
closestButton = button; closestButton = button;
closestDist = distance; closestDist = distance;
@@ -596,18 +637,39 @@ namespace Barotrauma
}); });
if (canAccess) if (canAccess)
{ {
bool pressButton = buttonPressCooldown <= 0 || lastDoor.door != door || lastDoor.state != shouldBeOpen;
if (door.HasIntegratedButtons) if (door.HasIntegratedButtons)
{ {
door.Item.TryInteract(character, false, true); if (pressButton && character.CanSeeTarget(door.Item))
buttonPressCooldown = ButtonPressInterval; {
if (door.Item.TryInteract(character, forceSelectKey: true))
{
lastDoor = (door, shouldBeOpen);
buttonPressCooldown = 3;
}
else
{
buttonPressCooldown = 0;
}
}
break; break;
} }
else if (closestButton != null) else if (closestButton != null)
{ {
if (Vector2.DistanceSquared(closestButton.Item.WorldPosition, character.WorldPosition) < MathUtils.Pow(closestButton.Item.InteractDistance + GetColliderLength(), 2)) if (closestDist < MathUtils.Pow2(closestButton.Item.InteractDistance + GetColliderLength()))
{ {
closestButton.Item.TryInteract(character, false, true); if (pressButton)
buttonPressCooldown = ButtonPressInterval; {
if (closestButton.Item.TryInteract(character, forceSelectKey: true))
{
lastDoor = (door, shouldBeOpen);
buttonPressCooldown = 3;
}
else
{
buttonPressCooldown = 0;
}
}
break; break;
} }
else else
@@ -627,6 +689,7 @@ namespace Barotrauma
// The button is on the wrong side of the door or a wall // The button is on the wrong side of the door or a wall
currentPath.Unreachable = true; currentPath.Unreachable = true;
} }
lastDoor = (null, false);
return; return;
} }
} }

View File

@@ -207,7 +207,8 @@ namespace Barotrauma
var cells = Level.Loaded.GetCells(character.WorldPosition, 1); var cells = Level.Loaded.GetCells(character.WorldPosition, 1);
if (cells.Count > 0) if (cells.Count > 0)
{ {
float closestDist = float.PositiveInfinity; //ignore walls more than 200 meters away
float closestDist = 200.0f * 200.0f;
foreach (Voronoi2.VoronoiCell cell in cells) foreach (Voronoi2.VoronoiCell cell in cells)
{ {
foreach (Voronoi2.GraphEdge edge in cell.Edges) foreach (Voronoi2.GraphEdge edge in cell.Edges)

View File

@@ -283,9 +283,9 @@ namespace Barotrauma
/// </summary> /// </summary>
public float CalculatePriority() public float CalculatePriority()
{ {
ForceWalk = false;
Priority = GetPriority(); Priority = GetPriority();
ForceHighestPriority = false; ForceHighestPriority = false;
ForceWalk = false;
return Priority; return Priority;
} }
@@ -501,5 +501,34 @@ namespace Barotrauma
} }
} }
} }
protected static bool CanEquip(Character character, Item item)
{
bool canEquip = item != null;
if (canEquip && !item.AllowedSlots.Contains(InvSlotType.Any))
{
canEquip = false;
var inv = character.Inventory;
foreach (var allowedSlot in item.AllowedSlots)
{
foreach (var slotType in inv.SlotTypes)
{
if (!allowedSlot.HasFlag(slotType)) { continue; }
for (int i = 0; i < inv.Capacity; i++)
{
canEquip = true;
if (allowedSlot.HasFlag(inv.SlotTypes[i]) && inv.GetItemAt(i) != null)
{
canEquip = false;
break;
}
}
}
}
}
return canEquip;
}
protected bool CanEquip(Item item) => CanEquip(character, item);
} }
} }

View File

@@ -68,15 +68,12 @@ namespace Barotrauma
protected override void OnObjectiveCompleted(AIObjective objective, Item target) protected override void OnObjectiveCompleted(AIObjective objective, Item target)
=> HumanAIController.RemoveTargets<AIObjectiveCleanupItems, Item>(character, target); => HumanAIController.RemoveTargets<AIObjectiveCleanupItems, Item>(character, target);
private static bool IsItemInsideValidSubmarine(Item item, Character character) public static bool IsItemInsideValidSubmarine(Item item, Character character)
{ {
if (item.CurrentHull == null) { return false; } if (item.CurrentHull == null) { return false; }
if (item.Submarine == null) { return false; } if (item.Submarine == null) { return false; }
if (item.Submarine.TeamID != character.TeamID) { return false; } if (item.Submarine.TeamID != character.TeamID) { return false; }
if (character.Submarine != null) if (character.Submarine != null && !character.Submarine.IsConnectedTo(item.Submarine)) { return false; }
{
if (!character.Submarine.IsConnectedTo(item.Submarine)) { return false; }
}
return true; return true;
} }
@@ -129,29 +126,7 @@ namespace Barotrauma
{ {
return true; return true;
} }
bool canEquip = true; return CanEquip(character, item);
if (!item.AllowedSlots.Contains(InvSlotType.Any))
{
canEquip = false;
var inv = character.Inventory;
foreach (var allowedSlot in item.AllowedSlots)
{
foreach (var slotType in inv.SlotTypes)
{
if (!allowedSlot.HasFlag(slotType)) { continue; }
for (int i = 0; i < inv.Capacity; i++)
{
canEquip = true;
if (allowedSlot.HasFlag(inv.SlotTypes[i]) && inv.GetItemAt(i) != null)
{
canEquip = false;
break;
}
}
}
}
}
return canEquip;
} }
public override void OnDeselected() public override void OnDeselected()

View File

@@ -111,7 +111,7 @@ namespace Barotrauma
public CombatMode Mode { get; private set; } public CombatMode Mode { get; private set; }
private bool IsOffensiveOrArrest => initialMode == CombatMode.Offensive || initialMode == CombatMode.Arrest; private bool IsOffensiveOrArrest => initialMode == CombatMode.Offensive || initialMode == CombatMode.Arrest;
private bool TargetEliminated => IsEnemyDisabled || (Enemy.IsUnconscious && Enemy.Params.Health.ConstantHealthRegeneration <= 0.0f); private bool TargetEliminated => IsEnemyDisabled || Enemy.IsUnconscious && Enemy.Params.Health.ConstantHealthRegeneration <= 0.0f || Enemy.IsHuman && Enemy.HasEquippedItem("handlocker") && !character.IsInstigator;
private bool IsEnemyDisabled => Enemy == null || Enemy.Removed || Enemy.IsDead; private bool IsEnemyDisabled => Enemy == null || Enemy.Removed || Enemy.IsDead;
private float AimSpeed => HumanAIController.AimSpeed; private float AimSpeed => HumanAIController.AimSpeed;
@@ -158,7 +158,7 @@ namespace Barotrauma
return Priority; return Priority;
} }
} }
float damageFactor = MathUtils.InverseLerp(0.0f, 5.0f, HumanAIController.GetDamageDoneByAttacker(Enemy) / 100.0f); float damageFactor = MathUtils.InverseLerp(0.0f, 5.0f, character.GetDamageDoneByAttacker(Enemy) / 100.0f);
Priority = TargetEliminated ? 0 : Math.Min((95 + damageFactor) * PriorityModifier, 100); Priority = TargetEliminated ? 0 : Math.Min((95 + damageFactor) * PriorityModifier, 100);
return Priority; return Priority;
} }
@@ -177,11 +177,12 @@ namespace Barotrauma
ignoredWeapons.Clear(); ignoredWeapons.Clear();
ignoreWeaponTimer = ignoredWeaponsClearTime; ignoreWeaponTimer = ignoredWeaponsClearTime;
} }
if (findSafety != null) bool isCurrentObjective = objectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>();
if (findSafety != null && isCurrentObjective)
{ {
findSafety.Priority = 0; findSafety.Priority = 0;
} }
if (!AllowCoolDown && !character.IsOnPlayerTeam && !objectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>()) if (!AllowCoolDown && !character.IsOnPlayerTeam && !isCurrentObjective)
{ {
distanceTimer -= deltaTime; distanceTimer -= deltaTime;
if (distanceTimer < 0) if (distanceTimer < 0)
@@ -204,13 +205,18 @@ namespace Barotrauma
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
if (IsEnemyDisabled)
{
IsCompleted = true;
return;
}
if (AllowCoolDown) if (AllowCoolDown)
{ {
coolDownTimer -= deltaTime; coolDownTimer -= deltaTime;
} }
if (seekAmmunitionObjective == null && seekWeaponObjective == null) if (seekAmmunitionObjective == null && seekWeaponObjective == null)
{ {
if (Mode != CombatMode.Retreat && TryArm() && !IsEnemyDisabled) if (Mode != CombatMode.Retreat && TryArm())
{ {
OperateWeapon(deltaTime); OperateWeapon(deltaTime);
} }
@@ -283,10 +289,12 @@ namespace Barotrauma
} }
else else
{ {
AskHelp();
Retreat(deltaTime); Retreat(deltaTime);
} }
break; break;
case CombatMode.Retreat: case CombatMode.Retreat:
AskHelp();
Retreat(deltaTime); Retreat(deltaTime);
break; break;
default: default:
@@ -363,6 +371,7 @@ namespace Barotrauma
{ {
if (WeaponComponent == null) if (WeaponComponent == null)
{ {
SpeakNoWeapons();
Mode = CombatMode.Retreat; Mode = CombatMode.Retreat;
} }
} }
@@ -409,6 +418,7 @@ namespace Barotrauma
onCompleted: () => RemoveSubObjective(ref seekWeaponObjective), onCompleted: () => RemoveSubObjective(ref seekWeaponObjective),
onAbandon: () => onAbandon: () =>
{ {
SpeakNoWeapons();
RemoveSubObjective(ref seekWeaponObjective); RemoveSubObjective(ref seekWeaponObjective);
Mode = CombatMode.Retreat; Mode = CombatMode.Retreat;
}); });
@@ -680,6 +690,7 @@ namespace Barotrauma
} }
else else
{ {
SpeakNoWeapons();
Weapon = null; Weapon = null;
Mode = CombatMode.Retreat; Mode = CombatMode.Retreat;
return false; return false;
@@ -892,7 +903,7 @@ namespace Barotrauma
TryAddSubObjective(ref seekAmmunitionObjective, TryAddSubObjective(ref seekAmmunitionObjective,
constructor: () => new AIObjectiveContainItem(character, ammunitionIdentifiers, Weapon.GetComponent<ItemContainer>(), objectiveManager) constructor: () => new AIObjectiveContainItem(character, ammunitionIdentifiers, Weapon.GetComponent<ItemContainer>(), objectiveManager)
{ {
targetItemCount = Weapon.GetComponent<ItemContainer>().Capacity, ItemCount = Weapon.GetComponent<ItemContainer>().Capacity,
checkInventory = false checkInventory = false
}, },
onCompleted: () => RemoveSubObjective(ref seekAmmunitionObjective), onCompleted: () => RemoveSubObjective(ref seekAmmunitionObjective),
@@ -1099,7 +1110,7 @@ namespace Barotrauma
if (WeaponComponent is RangedWeapon rangedWeapon) if (WeaponComponent is RangedWeapon rangedWeapon)
{ {
// If the weapon is just equipped, we can't shoot just yet. // If the weapon is just equipped, we can't shoot just yet.
if (rangedWeapon.ReloadTimer <= 0) if (rangedWeapon.ReloadTimer <= 0 && !rangedWeapon.HoldTrigger)
{ {
reloadTime = rangedWeapon.Reload; reloadTime = rangedWeapon.Reload;
} }
@@ -1155,6 +1166,21 @@ namespace Barotrauma
retreatTarget = null; retreatTarget = null;
} }
private void SpeakNoWeapons() => Speak("dialogcombatnoweapons", delay: 0, minDuration: 30);
private void AskHelp() => Speak("dialogcombatretreating", delay: Rand.Range(0, 1), minDuration: 20);
private void Speak(string textIdentifier, float delay, float minDuration)
{
if (character.IsOnPlayerTeam && !character.IsInFriendlySub)
{
string msg = TextManager.Get(textIdentifier, true);
if (msg != null)
{
character.Speak(msg, identifier: textIdentifier, delay: delay, minDurationBetweenSimilar: minDuration);
}
}
}
//private float CalculateEnemyStrength() //private float CalculateEnemyStrength()
//{ //{
// float enemyStrength = 0; // float enemyStrength = 0;

View File

@@ -11,12 +11,11 @@ namespace Barotrauma
public Func<Item, float> GetItemPriority; public Func<Item, float> GetItemPriority;
public int targetItemCount = 1;
public string[] ignoredContainerIdentifiers; public string[] ignoredContainerIdentifiers;
public bool checkInventory = true; public bool checkInventory = true;
//if the item can't be found, spawn it in the character's inventory (used by outpost NPCs) //if the item can't be found, spawn it in the character's inventory (used by outpost NPCs and in some cases also enemy NPCs, like pirates)
private bool spawnItemIfNotFound = false; private readonly bool spawnItemIfNotFound;
//can either be a tag or an identifier //can either be a tag or an identifier
public readonly string[] itemIdentifiers; public readonly string[] itemIdentifiers;
@@ -35,9 +34,24 @@ namespace Barotrauma
public bool Equip { get; set; } public bool Equip { get; set; }
public bool RemoveEmpty { get; set; } = true; public bool RemoveEmpty { get; set; } = true;
public bool RemoveExisting { get; set; } public bool RemoveExisting { get; set; }
/// <summary>
/// Only remove existing items when the contain target can't be put in the inventory
/// </summary>
public bool RemoveExistingWhenNecessary { get; set; }
public Func<Item, bool> RemoveExistingPredicate { get; set; }
public int? RemoveMax { get; set; }
public bool MoveWholeStack { get; set; } public bool MoveWholeStack { get; set; }
private int _itemCount = 1;
public int ItemCount
{
get { return _itemCount; }
set
{
_itemCount = Math.Max(value, 1);
}
}
public AIObjectiveContainItem(Character character, Item item, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1) public AIObjectiveContainItem(Character character, Item item, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier) : base(character, objectiveManager, priorityModifier)
@@ -83,7 +97,7 @@ namespace Barotrauma
containedItemCount++; containedItemCount++;
} }
} }
return containedItemCount >= targetItemCount; return containedItemCount >= ItemCount;
} }
} }
@@ -106,9 +120,9 @@ namespace Barotrauma
} }
if (character.CanInteractWith(container.Item, checkLinked: false)) if (character.CanInteractWith(container.Item, checkLinked: false))
{ {
if (RemoveExisting) if (RemoveExisting || (RemoveExistingWhenNecessary && !container.Inventory.CanBePut(item)))
{ {
HumanAIController.UnequipContainedItems(container.Item); HumanAIController.UnequipContainedItems(container.Item, predicate: RemoveExistingPredicate, unequipMax: RemoveMax);
} }
else if (RemoveEmpty) else if (RemoveEmpty)
{ {
@@ -127,7 +141,6 @@ namespace Barotrauma
container.Inventory.TryPutItem(item, null); container.Inventory.TryPutItem(item, null);
} }
} }
IsCompleted = true; IsCompleted = true;
} }
} }

View File

@@ -36,6 +36,11 @@ namespace Barotrauma
/// </summary> /// </summary>
public bool DropIfFails { get; set; } = true; public bool DropIfFails { get; set; } = true;
public bool RemoveExistingWhenNecessary { get; set; }
public Func<Item, bool> RemoveExistingPredicate { get; set; }
public int? RemoveExistingMax { get; set; }
public string AbandonGetItemDialogueIdentifier { get; set; }
public AIObjectiveDecontainItem(Character character, Item targetItem, AIObjectiveManager objectiveManager, ItemContainer sourceContainer = null, ItemContainer targetContainer = null, float priorityModifier = 1) public AIObjectiveDecontainItem(Character character, Item targetItem, AIObjectiveManager objectiveManager, ItemContainer sourceContainer = null, ItemContainer targetContainer = null, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier) : base(character, objectiveManager, priorityModifier)
{ {
@@ -86,6 +91,7 @@ namespace Barotrauma
} }
if (itemToDecontain.Container != sourceContainer.Item) if (itemToDecontain.Container != sourceContainer.Item)
{ {
itemToDecontain.Drop(character);
IsCompleted = true; IsCompleted = true;
return; return;
} }
@@ -98,7 +104,12 @@ namespace Barotrauma
if (getItemObjective == null && !itemToDecontain.IsOwnedBy(character)) if (getItemObjective == null && !itemToDecontain.IsOwnedBy(character))
{ {
TryAddSubObjective(ref getItemObjective, TryAddSubObjective(ref getItemObjective,
constructor: () => new AIObjectiveGetItem(character, targetItem, objectiveManager, Equip) { TakeWholeStack = this.TakeWholeStack }, constructor: () => new AIObjectiveGetItem(character, targetItem, objectiveManager, Equip)
{
CannotFindDialogueIdentifierOverride = AbandonGetItemDialogueIdentifier,
SpeakIfFails = AbandonGetItemDialogueIdentifier != null,
TakeWholeStack = this.TakeWholeStack
},
onAbandon: () => Abandon = true); onAbandon: () => Abandon = true);
return; return;
} }
@@ -110,6 +121,9 @@ namespace Barotrauma
MoveWholeStack = TakeWholeStack, MoveWholeStack = TakeWholeStack,
Equip = Equip, Equip = Equip,
RemoveEmpty = false, RemoveEmpty = false,
RemoveExistingWhenNecessary = RemoveExistingWhenNecessary,
RemoveExistingPredicate = RemoveExistingPredicate,
RemoveMax = RemoveExistingMax,
GetItemPriority = GetItemPriority, GetItemPriority = GetItemPriority,
ignoredContainerIdentifiers = sourceContainer != null ? new string[] { sourceContainer.Item.Prefab.Identifier } : null ignoredContainerIdentifiers = sourceContainer != null ? new string[] { sourceContainer.Item.Prefab.Identifier } : null
}, },

View File

@@ -31,7 +31,7 @@ namespace Barotrauma
protected override AIObjective ObjectiveConstructor(Character target) protected override AIObjective ObjectiveConstructor(Character target)
{ {
AIObjectiveCombat.CombatMode combatMode = target.IsEscorted && character.TeamID == CharacterTeamType.Team1 ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Offensive; AIObjectiveCombat.CombatMode combatMode = ShouldArrest(target, character) ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Offensive;
var combatObjective = new AIObjectiveCombat(character, target, combatMode, objectiveManager, PriorityModifier); var combatObjective = new AIObjectiveCombat(character, target, combatMode, objectiveManager, PriorityModifier);
if (character.TeamID == CharacterTeamType.FriendlyNPC && target.TeamID == CharacterTeamType.Team1 && GameMain.GameSession?.GameMode is CampaignMode campaign) if (character.TeamID == CharacterTeamType.FriendlyNPC && target.TeamID == CharacterTeamType.Team1 && GameMain.GameSession?.GameMode is CampaignMode campaign)
{ {
@@ -41,7 +41,7 @@ namespace Barotrauma
combatObjective.holdFireCondition = () => combatObjective.holdFireCondition = () =>
{ {
//hold fire while the enemy is in the airlock (except if they've attacked us) //hold fire while the enemy is in the airlock (except if they've attacked us)
if (HumanAIController.GetDamageDoneByAttacker(target) > 0.0f) { return false; } if (character.GetDamageDoneByAttacker(target) > 0.0f) { return false; }
return target.CurrentHull == null || target.CurrentHull.OutpostModuleTags.Any(t => t.Equals("airlock", System.StringComparison.OrdinalIgnoreCase)); return target.CurrentHull == null || target.CurrentHull.OutpostModuleTags.Any(t => t.Equals("airlock", System.StringComparison.OrdinalIgnoreCase));
}; };
character.Speak(TextManager.Get("dialogenteroutpostwarning"), null, Rand.Range(0.5f, 1.0f), "leaveoutpostwarning", 30.0f); character.Speak(TextManager.Get("dialogenteroutpostwarning"), null, Rand.Range(0.5f, 1.0f), "leaveoutpostwarning", 30.0f);
@@ -65,7 +65,13 @@ namespace Barotrauma
if (HumanAIController.IsFriendly(character, target)) { return false; } if (HumanAIController.IsFriendly(character, target)) { return false; }
if (!character.Submarine.IsConnectedTo(target.Submarine)) { return false; } if (!character.Submarine.IsConnectedTo(target.Submarine)) { return false; }
if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { return false; } if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { return false; }
if (ShouldArrest(target, character) && target.HasEquippedItem("handlocker")) { return false; }
return true; return true;
} }
public static bool ShouldArrest(Character target, Character character)
{
return target != null && target.IsEscorted && character.TeamID == CharacterTeamType.Team1;
}
} }
} }

View File

@@ -52,9 +52,11 @@ namespace Barotrauma
} }
else else
{ {
Priority = (objectiveManager.IsCurrentOrder<AIObjectiveGoTo>() || Priority = (
objectiveManager.IsCurrentOrder<AIObjectiveReturn>() || objectiveManager.HasOrder<AIObjectiveGoTo>(o => o.Priority > 0) ||
objectiveManager.Objectives.Any(o => o.Priority > 0 && o is AIObjectiveCombat)) objectiveManager.HasOrder<AIObjectiveReturn>(o => o.Priority > 0) ||
objectiveManager.HasActiveObjective<AIObjectiveRescue>() ||
objectiveManager.Objectives.Any(o => o is AIObjectiveCombat && o.Priority > 0))
&& HumanAIController.HasDivingSuit(character) ? 0 : 100; && HumanAIController.HasDivingSuit(character) ? 0 : 100;
} }
} }

View File

@@ -21,7 +21,7 @@ namespace Barotrauma
public float TargetCondition { get; set; } = 1; public float TargetCondition { get; set; } = 1;
public bool AllowDangerousPressure { get; set; } public bool AllowDangerousPressure { get; set; }
private readonly ImmutableArray<string> identifiersOrTags; public readonly ImmutableArray<string> IdentifiersOrTags;
//if the item can't be found, spawn it in the character's inventory (used by outpost NPCs) //if the item can't be found, spawn it in the character's inventory (used by outpost NPCs)
private bool spawnItemIfNotFound = false; private bool spawnItemIfNotFound = false;
@@ -58,6 +58,17 @@ namespace Barotrauma
public bool EvaluateCombatPriority { get; set; } public bool EvaluateCombatPriority { get; set; }
public bool CheckPathForEachItem { get; set; } public bool CheckPathForEachItem { get; set; }
public bool SpeakIfFails { get; set; } public bool SpeakIfFails { get; set; }
public string CannotFindDialogueIdentifierOverride { get; set; }
private int _itemCount = 1;
public int ItemCount
{
get { return _itemCount; }
set
{
_itemCount = Math.Max(value, 1);
}
}
public InvSlotType? EquipSlotType { get; set; } public InvSlotType? EquipSlotType { get; set; }
@@ -81,7 +92,7 @@ namespace Barotrauma
Equip = equip; Equip = equip;
this.spawnItemIfNotFound = spawnItemIfNotFound; this.spawnItemIfNotFound = spawnItemIfNotFound;
this.checkInventory = checkInventory; this.checkInventory = checkInventory;
this.identifiersOrTags = ParseGearTags(identifiersOrTags).ToImmutableArray(); IdentifiersOrTags = ParseGearTags(identifiersOrTags).ToImmutableArray();
ignoredIdentifiersOrTags = ParseIgnoredTags(identifiersOrTags).ToArray(); ignoredIdentifiersOrTags = ParseIgnoredTags(identifiersOrTags).ToArray();
} }
@@ -113,7 +124,7 @@ namespace Barotrauma
private bool CheckInventory() private bool CheckInventory()
{ {
if (identifiersOrTags == null) { return false; } if (IdentifiersOrTags == null) { return false; }
var item = character.Inventory.FindItem(i => CheckItem(i), recursive: true); var item = character.Inventory.FindItem(i => CheckItem(i), recursive: true);
if (item != null) if (item != null)
{ {
@@ -123,6 +134,19 @@ namespace Barotrauma
return item != null; return item != null;
} }
private bool CountItems()
{
int itemCount = 0;
foreach (Item it in character.Inventory.AllItems)
{
if (CheckItem(it))
{
itemCount++;
}
}
return itemCount >= ItemCount;
}
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
if (character.LockHands) if (character.LockHands)
@@ -135,7 +159,7 @@ namespace Barotrauma
Abandon = true; Abandon = true;
return; return;
} }
if (identifiersOrTags != null && !isDoneSeeking) if (IdentifiersOrTags != null && !isDoneSeeking)
{ {
if (checkInventory) if (checkInventory)
{ {
@@ -152,7 +176,7 @@ namespace Barotrauma
if (dangerousPressure) if (dangerousPressure)
{ {
#if DEBUG #if DEBUG
string itemName = targetItem != null ? targetItem.Name : identifiersOrTags.FirstOrDefault(); string itemName = targetItem != null ? targetItem.Name : IdentifiersOrTags.FirstOrDefault();
DebugConsole.NewMessage($"{character.Name}: Seeking item ({itemName}) aborted, because the pressure is dangerous.", Color.Yellow); DebugConsole.NewMessage($"{character.Name}: Seeking item ({itemName}) aborted, because the pressure is dangerous.", Color.Yellow);
#endif #endif
Abandon = true; Abandon = true;
@@ -245,7 +269,18 @@ namespace Barotrauma
} }
} }
} }
IsCompleted = true; if (IdentifiersOrTags == null)
{
IsCompleted = true;
}
else
{
IsCompleted = CountItems();
if (!IsCompleted)
{
ResetInternal();
}
}
} }
else else
{ {
@@ -297,7 +332,7 @@ namespace Barotrauma
private void FindTargetItem() private void FindTargetItem()
{ {
if (identifiersOrTags == null) if (IdentifiersOrTags == null)
{ {
if (targetItem == null) if (targetItem == null)
{ {
@@ -316,7 +351,7 @@ namespace Barotrauma
// Otherwise it will take some time for us to find a valid item when there are multiple items that we can't reach and some that we can. // Otherwise it will take some time for us to find a valid item when there are multiple items that we can't reach and some that we can.
// This is relatively expensive, so let's do this only when it significantly improves the behavior. // This is relatively expensive, so let's do this only when it significantly improves the behavior.
// Only allow one path find call per frame. // Only allow one path find call per frame.
CheckPathForEachItem = priority >= AIObjectiveManager.LowestOrderPriority && (objectiveManager.IsCurrentOrder<AIObjectiveFixLeaks>() || objectiveManager.CurrentOrder is AIObjectiveGoTo gotoOrder && gotoOrder.isFollowOrderObjective); CheckPathForEachItem = priority >= AIObjectiveManager.LowestOrderPriority && (objectiveManager.IsCurrentOrder<AIObjectiveFixLeaks>() || objectiveManager.CurrentOrder is AIObjectiveGoTo gotoOrder && gotoOrder.IsFollowOrderObjective);
} }
bool checkPath = CheckPathForEachItem; bool checkPath = CheckPathForEachItem;
bool hasCalledPathFinder = false; bool hasCalledPathFinder = false;
@@ -430,10 +465,10 @@ namespace Barotrauma
{ {
if (spawnItemIfNotFound) if (spawnItemIfNotFound)
{ {
if (!(MapEntityPrefab.List.FirstOrDefault(me => me is ItemPrefab ip && identifiersOrTags.Any(id => id == ip.Identifier || ip.Tags.Contains(id))) is ItemPrefab prefab)) if (!(MapEntityPrefab.List.FirstOrDefault(me => me is ItemPrefab ip && IdentifiersOrTags.Any(id => id == ip.Identifier || ip.Tags.Contains(id))) is ItemPrefab prefab))
{ {
#if DEBUG #if DEBUG
DebugConsole.NewMessage($"{character.Name}: Cannot find an item with the following identifier(s) or tag(s): {string.Join(", ", identifiersOrTags)}, tried to spawn the item but no matching item prefabs were found.", Color.Yellow); DebugConsole.NewMessage($"{character.Name}: Cannot find an item with the following identifier(s) or tag(s): {string.Join(", ", IdentifiersOrTags)}, tried to spawn the item but no matching item prefabs were found.", Color.Yellow);
#endif #endif
Abandon = true; Abandon = true;
} }
@@ -452,7 +487,7 @@ namespace Barotrauma
else else
{ {
#if DEBUG #if DEBUG
DebugConsole.NewMessage($"{character.Name}: Cannot find an item with the following identifier(s) or tag(s): {string.Join(", ", identifiersOrTags)}", Color.Yellow); DebugConsole.NewMessage($"{character.Name}: Cannot find an item with the following identifier(s) or tag(s): {string.Join(", ", IdentifiersOrTags)}", Color.Yellow);
#endif #endif
Abandon = true; Abandon = true;
} }
@@ -468,13 +503,20 @@ namespace Barotrauma
// Not yet ready // Not yet ready
return false; return false;
} }
if (Equip && EquipSlotType.HasValue) if (IdentifiersOrTags != null && ItemCount > 1)
{ {
return character.HasEquippedItem(targetItem, EquipSlotType.Value); return CountItems();
} }
else else
{ {
return character.HasItem(targetItem, Equip); if (Equip && EquipSlotType.HasValue)
{
return character.HasEquippedItem(targetItem, EquipSlotType.Value);
}
else
{
return character.HasItem(targetItem, Equip);
}
} }
} }
@@ -487,7 +529,7 @@ namespace Barotrauma
if (item.Condition < TargetCondition) { return false; } if (item.Condition < TargetCondition) { return false; }
if (ItemFilter != null && !ItemFilter(item)) { return false; } if (ItemFilter != null && !ItemFilter(item)) { return false; }
if (RequireLoaded && item.Components.Any(i => !i.IsLoaded(character))) { return false; } if (RequireLoaded && item.Components.Any(i => !i.IsLoaded(character))) { return false; }
return identifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id) || (AllowVariants && item.Prefab.VariantOf?.Identifier == id)); return IdentifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id) || (AllowVariants && item.Prefab.VariantOf?.Identifier == id));
} }
public override void Reset() public override void Reset()
@@ -528,7 +570,7 @@ namespace Barotrauma
{ {
if (character.IsOnPlayerTeam && objectiveManager.CurrentOrder == objectiveManager.CurrentObjective) if (character.IsOnPlayerTeam && objectiveManager.CurrentOrder == objectiveManager.CurrentObjective)
{ {
string msg = TextManager.Get("dialogcannotfinditem", true); string msg = TextManager.Get(CannotFindDialogueIdentifierOverride, returnNull: true) ?? TextManager.Get("dialogcannotfinditem", returnNull: true);
if (msg != null) if (msg != null)
{ {
character.Speak(msg, identifier: "dialogcannotfinditem", minDurationBetweenSimilar: 20.0f); character.Speak(msg, identifier: "dialogcannotfinditem", minDurationBetweenSimilar: 20.0f);

View File

@@ -11,6 +11,7 @@ namespace Barotrauma
public override string Identifier { get; set; } = "get items"; public override string Identifier { get; set; } = "get items";
public override string DebugTag => $"{Identifier}"; public override string DebugTag => $"{Identifier}";
public override bool KeepDivingGearOn => true; public override bool KeepDivingGearOn => true;
public override bool AllowMultipleInstances => true;
public bool AllowStealing { get; set; } public bool AllowStealing { get; set; }
public bool TakeWholeStack { get; set; } public bool TakeWholeStack { get; set; }
@@ -21,6 +22,7 @@ namespace Barotrauma
public bool EvaluateCombatPriority { get; set; } public bool EvaluateCombatPriority { get; set; }
public bool CheckPathForEachItem { get; set; } public bool CheckPathForEachItem { get; set; }
public bool RequireLoaded { get; set; } public bool RequireLoaded { get; set; }
public bool RequireAllItems { get; set; }
private readonly ImmutableArray<string> gearTags; private readonly ImmutableArray<string> gearTags;
private readonly string[] ignoredTags; private readonly string[] ignoredTags;
@@ -47,9 +49,11 @@ namespace Barotrauma
{ {
foreach (string tag in gearTags) foreach (string tag in gearTags)
{ {
if (subObjectives.Any(so => so is AIObjectiveGetItem getItem && getItem.IdentifiersOrTags.Contains(tag))) { continue; }
int count = gearTags.Count(t => t == tag);
AIObjectiveGetItem? getItem = null; AIObjectiveGetItem? getItem = null;
TryAddSubObjective(ref getItem, () => TryAddSubObjective(ref getItem, () =>
new AIObjectiveGetItem(character, tag, objectiveManager, Equip, CheckInventory) new AIObjectiveGetItem(character, tag, objectiveManager, Equip, CheckInventory && count <= 1)
{ {
AllowVariants = AllowVariants, AllowVariants = AllowVariants,
Wear = Wear, Wear = Wear,
@@ -57,7 +61,9 @@ namespace Barotrauma
AllowStealing = AllowStealing, AllowStealing = AllowStealing,
ignoredIdentifiersOrTags = ignoredTags, ignoredIdentifiersOrTags = ignoredTags,
CheckPathForEachItem = CheckPathForEachItem, CheckPathForEachItem = CheckPathForEachItem,
RequireLoaded = RequireLoaded RequireLoaded = RequireLoaded,
ItemCount = count,
SpeakIfFails = RequireAllItems
}, },
onCompleted: () => onCompleted: () =>
{ {
@@ -75,6 +81,10 @@ namespace Barotrauma
achievedItems.Remove(item); achievedItems.Remove(item);
} }
RemoveSubObjective(ref getItem); RemoveSubObjective(ref getItem);
if (RequireAllItems)
{
Abandon = true;
}
}); });
} }
subObjectivesCreated = true; subObjectivesCreated = true;

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