Unstable v0.15.17.0 (Hex is out of town edition)
This commit is contained in:
@@ -63,7 +63,10 @@ namespace Barotrauma
|
||||
{
|
||||
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;
|
||||
@@ -98,6 +101,16 @@ namespace Barotrauma
|
||||
if (distSqrd > 10.0f || !character.CanMove)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
@@ -194,7 +207,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.SelectedConstruction != serverPos.SelectedItem)
|
||||
{
|
||||
serverPos.SelectedItem.TryInteract(character, true, true);
|
||||
serverPos.SelectedItem.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true);
|
||||
}
|
||||
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());
|
||||
GameAnalyticsManager.AddErrorEventOnce("Ragdoll.Draw:LimbsRemoved",
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.SpeciesName + "\", removed: " + character.Removed + "\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -460,12 +473,17 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
var limb = inversedLimbDrawOrder[i];
|
||||
if (depthOffset != 0.0f) { limb.ActiveSprite.Depth += depthOffset; }
|
||||
limb.Draw(spriteBatch, cam, color);
|
||||
if (depthOffset != 0.0f) { limb.ActiveSprite.Depth -= depthOffset; }
|
||||
inversedLimbDrawOrder[i].Draw(spriteBatch, cam, color);
|
||||
}
|
||||
if (!MathUtils.NearlyEqual(depthOffset, 0.0f))
|
||||
{
|
||||
foreach (Limb limb in limbs) { limb.ActiveSprite.Depth -= depthOffset; }
|
||||
}
|
||||
LimbJoints.ForEach(j => j.Draw(spriteBatch));
|
||||
}
|
||||
@@ -486,9 +504,16 @@ namespace Barotrauma
|
||||
if (character.WorldPosition.X < character.SelectedConstruction.WorldPosition.X)
|
||||
{
|
||||
//at the left side of the ladder, needs to be drawn in front of the rungs
|
||||
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
|
||||
{
|
||||
//at the right side of the ladder, needs to be drawn behind the rungs
|
||||
depthOffset = Math.Max(ladder.BackgroundSpriteDepth + 0.01f - minDepth, 0.0f);
|
||||
|
||||
@@ -305,9 +305,9 @@ namespace Barotrauma
|
||||
case 0: //NetEntityEvent.Type.InventoryState
|
||||
if (Inventory == null)
|
||||
{
|
||||
string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
string errorMsg = "Received an inventory update message for an entity with no inventory ([name], removed: " + Removed + ")";
|
||||
DebugConsole.ThrowError(errorMsg.Replace("[name]", Name));
|
||||
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
|
||||
_ = msg.ReadUInt16();
|
||||
@@ -651,7 +651,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = $"Error in CharacterNetworking.ReadStatus: affliction not found ({afflictionName})";
|
||||
causeOfDeathType = CauseOfDeathType.Unknown;
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionIndexOutOfBounts", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -682,7 +682,7 @@ namespace Barotrauma
|
||||
if (severedJointIndex < 0 || severedJointIndex >= AnimController.LimbJoints.Length)
|
||||
{
|
||||
string errorMsg = $"Error in CharacterNetworking.ReadStatus: severed joint index out of bounds (index: {severedJointIndex}, joint count: {AnimController.LimbJoints.Length})";
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:JointIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:JointIndexOutOfBounts", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -453,13 +453,14 @@ namespace Barotrauma
|
||||
{
|
||||
case Alignment.Left:
|
||||
healthWindow.RectTransform.SetPosition(Anchor.BottomLeft);
|
||||
healthWindow.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.InventoryAreaLower.X, screenResolution.Y - HUDLayoutSettings.ChatBoxArea.Y + HUDLayoutSettings.Padding);
|
||||
break;
|
||||
case Alignment.Right:
|
||||
healthWindow.RectTransform.SetPosition(Anchor.BottomRight);
|
||||
healthWindow.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.Padding, screenResolution.Y - HUDLayoutSettings.ChatBoxArea.Y + HUDLayoutSettings.Padding);
|
||||
break;
|
||||
}
|
||||
|
||||
healthWindow.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.Padding, screenResolution.Y - HUDLayoutSettings.ChatBoxArea.Y + HUDLayoutSettings.Padding);
|
||||
healthWindow.RectTransform.RecalculateChildren(false);
|
||||
}
|
||||
|
||||
@@ -648,8 +649,9 @@ namespace Barotrauma
|
||||
grainColor = oxygenLowGrainColor;
|
||||
}
|
||||
|
||||
foreach (Affliction affliction in afflictions)
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
{
|
||||
var affliction = kvp.Key;
|
||||
distortStrength = Math.Max(distortStrength, affliction.GetScreenDistortStrength());
|
||||
blurStrength = Math.Max(blurStrength, affliction.GetScreenBlurStrength());
|
||||
radialDistortStrength = Math.Max(radialDistortStrength, affliction.GetRadialDistortStrength());
|
||||
@@ -662,16 +664,6 @@ namespace Barotrauma
|
||||
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.ChromaticAberrationStrength = chromaticAberrationStrength;
|
||||
@@ -777,7 +769,7 @@ namespace Barotrauma
|
||||
{
|
||||
// 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();
|
||||
if (affliction.DamagePerSecond > 0 || affliction.Strength > 0)
|
||||
if (affliction != null && (affliction.DamagePerSecond > 0 || affliction.Strength > 0))
|
||||
{
|
||||
var limbHealth = GetMatchingLimbHealth(affliction);
|
||||
if (limbHealth != null)
|
||||
@@ -788,7 +780,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -971,8 +963,9 @@ namespace Barotrauma
|
||||
UpdateAlignment();
|
||||
}
|
||||
|
||||
foreach (Affliction affliction in afflictions)
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
{
|
||||
var affliction = kvp.Key;
|
||||
if (affliction.Prefab.AfflictionOverlay != null)
|
||||
{
|
||||
Sprite ScreenAfflictionOverlay = affliction.Prefab.AfflictionOverlay;
|
||||
@@ -984,7 +977,7 @@ namespace Barotrauma
|
||||
float damageOverlayAlpha = DamageOverlayTimer;
|
||||
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
|
||||
{
|
||||
@@ -1159,18 +1152,34 @@ namespace Barotrauma
|
||||
afflictionIconContainer.Content.ClearChildren();
|
||||
return;
|
||||
}
|
||||
var currentAfflictions = GetMatchingAfflictions(selectedLimb, a => a.ShouldShowIcon(Character));
|
||||
if (currentAfflictions.Any(a => !displayedAfflictions.Any(d => d.affliction == a)) ||
|
||||
displayedAfflictions.Any(a => !currentAfflictions.Contains(a.affliction)))
|
||||
|
||||
if (afflictionsDirty())
|
||||
{
|
||||
var currentAfflictions = afflictions.Where(a => a.Value == selectedLimb && a.Key.ShouldShowIcon(Character)).Select(a => a.Key);
|
||||
CreateAfflictionInfos(currentAfflictions);
|
||||
CreateRecommendedTreatments();
|
||||
}
|
||||
//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();
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -1604,7 +1613,7 @@ namespace Barotrauma
|
||||
int i = 0;
|
||||
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);
|
||||
|
||||
@@ -1623,6 +1632,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<Affliction> afflictionsDisplayedOnLimb = new List<Affliction>();
|
||||
private void DrawHealthWindow(SpriteBatch spriteBatch, Rectangle drawArea, bool allowHighlight)
|
||||
{
|
||||
if (Character.Removed) { return; }
|
||||
@@ -1633,21 +1643,32 @@ namespace Barotrauma
|
||||
int i = 0;
|
||||
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,
|
||||
limbHealth.IndicatorSprite.SourceRect.Y + limbHealth.HighlightArea.Y,
|
||||
limbHealth.HighlightArea.Width,
|
||||
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 negativeMaxEffect = tempAfflictions.Where(a => !a.Prefab.IsBuff).Sum(a => a.Prefab.MaxStrength);
|
||||
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);
|
||||
float negativeEffect = 0.0f, positiveEffect = 0.0f;
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
{
|
||||
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 fadeDist = 0.6f * (float)limbEffectiveArea.Height / (float)limbHealth.IndicatorSprite.Texture.Height;
|
||||
@@ -1714,7 +1735,7 @@ namespace Barotrauma
|
||||
drawArea.Width / (float)limbIndicatorOverlay.FrameSize.X,
|
||||
drawArea.Height / (float)limbIndicatorOverlay.FrameSize.Y);
|
||||
|
||||
int frame = 0;
|
||||
int frame;
|
||||
int frameCount = 17;
|
||||
if (limbIndicatorOverlayAnimState >= frameCount * 2) limbIndicatorOverlayAnimState = 0.0f;
|
||||
if (limbIndicatorOverlayAnimState < frameCount)
|
||||
@@ -1758,14 +1779,28 @@ namespace Barotrauma
|
||||
i = 0;
|
||||
foreach (LimbHealth limbHealth in limbHealths)
|
||||
{
|
||||
IEnumerable<Affliction> thisAfflictions = limbHealth.Afflictions.Where(a => a.ShouldShowIcon(Character));
|
||||
thisAfflictions = thisAfflictions.Concat(afflictions.Where(a =>
|
||||
bool shouldDisplayAffliction(KeyValuePair<Affliction, LimbHealth> kvp, LimbHealth limbHealth)
|
||||
{
|
||||
Limb indicatorLimb = Character.AnimController.GetLimb(a.Prefab.IndicatorLimb);
|
||||
return indicatorLimb != null && indicatorLimb.HealthIndex == i && a.ShouldShowIcon(Character);
|
||||
}));
|
||||
if (!kvp.Key.ShouldShowIcon(Character)) { return false; }
|
||||
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; }
|
||||
|
||||
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();
|
||||
|
||||
//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 (thisAfflictions.Count() > 1)
|
||||
if (afflictionsDisplayedOnLimb.Count() > 1)
|
||||
{
|
||||
string additionalAfflictionCount = $"+{thisAfflictions.Count() - 1}";
|
||||
string additionalAfflictionCount = $"+{afflictionsDisplayedOnLimb.Count() - 1}";
|
||||
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, -displace.Y * 0.5f), Color.White);
|
||||
@@ -1834,8 +1869,7 @@ namespace Barotrauma
|
||||
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)> newLimbAfflictions = new List<(LimbHealth limb, 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<(AfflictionPrefab.PeriodicEffect effect, float timer)> newPeriodicEffects = new List<(AfflictionPrefab.PeriodicEffect effect, float timer)>();
|
||||
|
||||
public void ClientRead(IReadMessage inc)
|
||||
@@ -1865,47 +1899,9 @@ namespace Barotrauma
|
||||
float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
|
||||
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();
|
||||
for (int i = 0; i < limbAfflictionCount; i++)
|
||||
{
|
||||
@@ -1931,31 +1927,39 @@ namespace Barotrauma
|
||||
float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
|
||||
newPeriodicEffects.Add((afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
|
||||
}
|
||||
newLimbAfflictions.Add((limbHealths[limbIndex], afflictionPrefab, afflictionStrength));
|
||||
newAfflictions.Add((limbHealths[limbIndex], afflictionPrefab, afflictionStrength));
|
||||
}
|
||||
|
||||
foreach (LimbHealth limbHealth in limbHealths)
|
||||
{
|
||||
foreach (Affliction affliction in limbHealth.Afflictions)
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
{
|
||||
//deactivate afflictions that weren't included in the network message
|
||||
if (!newLimbAfflictions.Any(a => a.limb == limbHealth && a.afflictionPrefab == affliction.Prefab))
|
||||
if (!newAfflictions.Any(a => kvp.Key.Prefab == a.afflictionPrefab && kvp.Value == a.limb))
|
||||
{
|
||||
affliction.Strength = 0.0f;
|
||||
kvp.Key.Strength = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (limb, afflictionPrefab, strength) in newLimbAfflictions)
|
||||
foreach (var (limb, afflictionPrefab, strength) in newAfflictions)
|
||||
{
|
||||
if (limb != limbHealth) { continue; }
|
||||
Affliction existingAffliction = limbHealth.Afflictions.Find(a => a.Prefab == afflictionPrefab);
|
||||
Affliction existingAffliction = null;
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
{
|
||||
if (kvp.Key.Prefab == afflictionPrefab && kvp.Value == limb)
|
||||
{
|
||||
existingAffliction = kvp.Key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (existingAffliction == null)
|
||||
{
|
||||
existingAffliction = afflictionPrefab.Instantiate(strength);
|
||||
limbHealth.Afflictions.Add(existingAffliction);
|
||||
afflictions.Add(existingAffliction, limb);
|
||||
}
|
||||
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; }
|
||||
@@ -1971,7 +1975,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CalculateVitality();
|
||||
DisplayedVitality = Vitality;
|
||||
@@ -1985,14 +1988,13 @@ namespace Barotrauma
|
||||
|
||||
limb.BurnOverlayStrength = 0.0f;
|
||||
limb.DamageOverlayStrength = 0.0f;
|
||||
if (limbHealths[limb.HealthIndex].Afflictions.Count == 0) continue;
|
||||
foreach (Affliction a in limbHealths[limb.HealthIndex].Afflictions)
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
{
|
||||
limb.BurnOverlayStrength += a.Strength / Math.Min(a.Prefab.MaxStrength, 100) * a.Prefab.BurnOverlayAlpha;
|
||||
limb.DamageOverlayStrength += a.Strength / Math.Min(a.Prefab.MaxStrength, 100) * a.Prefab.DamageOverlayAlpha;
|
||||
if (kvp.Value != limbHealths[limb.HealthIndex]) { continue; }
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -553,6 +553,12 @@ namespace Barotrauma
|
||||
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);
|
||||
|
||||
}, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().ToArray() }));
|
||||
@@ -1461,10 +1467,11 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (ItemPrefab ingredientItemPrefab in ingredient.ItemPrefabs)
|
||||
{
|
||||
NewMessage(" Its ingredient " + ingredientItemPrefab.Name + " has base cost " + ingredientItemPrefab.DefaultPrice.Price);
|
||||
totalPrice += ingredientItemPrefab.DefaultPrice.Price;
|
||||
int defaultPrice = ingredientItemPrefab.DefaultPrice?.Price ?? 0;
|
||||
NewMessage(" Its ingredient " + ingredientItemPrefab.Name + " has base cost " + defaultPrice);
|
||||
totalPrice += defaultPrice;
|
||||
totalBestPrice += ingredientItemPrefab.GetMinPrice();
|
||||
int basePrice = ingredientItemPrefab.DefaultPrice.Price;
|
||||
int basePrice = defaultPrice;
|
||||
foreach (KeyValuePair<string, PriceInfo> ingredientItemLocationPrice in ingredientItemPrefab.GetBuyPricesUnder())
|
||||
{
|
||||
if (basePrice > ingredientItemLocationPrice.Value.Price)
|
||||
@@ -1630,7 +1637,7 @@ namespace Barotrauma
|
||||
|
||||
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == parentItem);
|
||||
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)
|
||||
{
|
||||
NewMessage(" It constructs from:");
|
||||
@@ -1639,8 +1646,9 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
NewMessage(" " + itemPrefab.Name + " has the price " + itemPrefab.DefaultPrice.Price);
|
||||
totalValue += itemPrefab.DefaultPrice.Price;
|
||||
int defaultPrice = itemPrefab.DefaultPrice?.Price ?? 0;
|
||||
NewMessage(" " + itemPrefab.Name + " has the price " + defaultPrice);
|
||||
totalValue += defaultPrice;
|
||||
}
|
||||
}
|
||||
NewMessage("Its total value was: " + totalValue);
|
||||
@@ -1651,10 +1659,16 @@ namespace Barotrauma
|
||||
{
|
||||
ItemPrefab itemPrefab =
|
||||
(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);
|
||||
totalValue += itemPrefab.DefaultPrice.Price;
|
||||
int defaultPrice = itemPrefab.DefaultPrice?.Price ?? 0;
|
||||
NewMessage(" " + itemPrefab.Name + " has the price " + defaultPrice);
|
||||
totalValue += defaultPrice;
|
||||
}
|
||||
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("Fullscreen enabled", Color.Green);
|
||||
|
||||
GameSettings.ShowUserStatisticsPrompt = true;
|
||||
|
||||
GameSettings.VerboseLogging = false;
|
||||
|
||||
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) + "\".";
|
||||
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());
|
||||
}
|
||||
},
|
||||
() =>
|
||||
|
||||
@@ -363,7 +363,7 @@ namespace Barotrauma
|
||||
OnSecondaryClicked = (_, o) =>
|
||||
{
|
||||
if (!(o is Client client)) { return false; }
|
||||
GameMain.GameSession?.CrewManager?.CreateModerationContextMenu(PlayerInput.MousePosition.ToPoint(), client);
|
||||
NetLobbyScreen.CreateModerationContextMenu(client);
|
||||
return true;
|
||||
},
|
||||
Text = senderName
|
||||
@@ -397,6 +397,7 @@ namespace Barotrauma
|
||||
if (GameMain.NetLobbyScreen != null && GameMain.NetworkMember != null)
|
||||
{
|
||||
clickableArea.OnClick = GameMain.NetLobbyScreen.SelectPlayer;
|
||||
clickableArea.OnSecondaryClick = GameMain.NetLobbyScreen.ShowPlayerContextMenu;
|
||||
}
|
||||
msgText.ClickableAreas.Add(clickableArea);
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@ namespace Barotrauma
|
||||
|
||||
public bool AllowMouseWheelScroll { get; set; } = true;
|
||||
|
||||
public bool AllowArrowKeyScroll { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Scrolls the list smoothly
|
||||
/// </summary>
|
||||
@@ -1254,10 +1256,16 @@ namespace Barotrauma
|
||||
switch (key)
|
||||
{
|
||||
case Keys.Down:
|
||||
SelectNext();
|
||||
if (!isHorizontal && AllowArrowKeyScroll) { SelectNext(); }
|
||||
break;
|
||||
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;
|
||||
case Keys.Enter:
|
||||
case Keys.Space:
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"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());
|
||||
return;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ namespace Barotrauma
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"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());
|
||||
}
|
||||
else
|
||||
|
||||
@@ -276,6 +276,7 @@ namespace Barotrauma
|
||||
|
||||
public delegate void OnClickDelegate(GUITextBlock textBlock, ClickableArea area);
|
||||
public OnClickDelegate OnClick;
|
||||
public OnClickDelegate OnSecondaryClick;
|
||||
}
|
||||
public List<ClickableArea> ClickableAreas { get; private set; } = new List<ClickableArea>();
|
||||
|
||||
@@ -528,7 +529,7 @@ namespace Barotrauma
|
||||
{
|
||||
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; }
|
||||
int index = GetCaretIndexFromScreenPos(PlayerInput.MousePosition);
|
||||
@@ -541,6 +542,10 @@ namespace Barotrauma
|
||||
{
|
||||
clickableArea.OnClick?.Invoke(this, clickableArea);
|
||||
}
|
||||
if (PlayerInput.SecondaryMouseButtonClicked())
|
||||
{
|
||||
clickableArea.OnSecondaryClick?.Invoke(this, clickableArea);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,7 +562,7 @@ namespace Barotrauma
|
||||
|
||||
frame.OnSecondaryClicked += (component, data) =>
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.CreateModerationContextMenu(PlayerInput.MousePosition.ToPoint(), client);
|
||||
NetLobbyScreen.CreateModerationContextMenu(client);
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -917,7 +917,8 @@ namespace Barotrauma
|
||||
textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea()
|
||||
{
|
||||
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),
|
||||
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)
|
||||
{
|
||||
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 };
|
||||
@@ -1643,6 +1645,7 @@ namespace Barotrauma
|
||||
GameMain.Client.CreateEntityEvent(controlledCharacter, new object[] { NetEntityEvent.Type.UpdateTalents });
|
||||
}
|
||||
}
|
||||
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
|
||||
UpdateTalentButtons();
|
||||
}
|
||||
|
||||
|
||||
@@ -846,23 +846,27 @@ namespace Barotrauma
|
||||
|
||||
var currentOrPending = item.PendingItemSwap ?? item.Prefab;
|
||||
string name = currentOrPending.Name;
|
||||
string quantityText = "";
|
||||
string nameWithQuantity = "";
|
||||
if (linkedItems.Count > 1)
|
||||
{
|
||||
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);
|
||||
quantityText += distinctItem.Name;
|
||||
nameWithQuantity += distinctItem.Name;
|
||||
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;
|
||||
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);
|
||||
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)
|
||||
{
|
||||
TextColor = GUI.Style.Orange
|
||||
@@ -907,7 +911,7 @@ namespace Barotrauma
|
||||
if (isUninstallPending) { canUninstall = false; }
|
||||
|
||||
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,
|
||||
0, null, addBuyButton: canUninstall, addProgressBar: false, buttonStyle: "WeaponUninstallButton"));
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using GameAnalyticsSDK.Net;
|
||||
using Barotrauma.IO;
|
||||
using System.Threading;
|
||||
using Barotrauma.Tutorials;
|
||||
@@ -384,51 +383,6 @@ namespace Barotrauma
|
||||
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 LoadingException(Exception e) : base("Loading was interrupted due to an error.", innerException: e)
|
||||
@@ -522,12 +476,7 @@ namespace Barotrauma
|
||||
DebugConsole.Log("Selected content packages: " + string.Join(", ", Config.AllEnabledPackages.Select(cp => cp.Name)));
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
GameSettings.ShowUserStatisticsPrompt = false;
|
||||
GameSettings.SendUserStatistics = false;
|
||||
#endif
|
||||
|
||||
InitUserStats();
|
||||
GameAnalyticsManager.InitIfConsented();
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
@@ -841,6 +790,8 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
|
||||
NetworkMember?.Update((float)Timing.Step);
|
||||
|
||||
if (!hasLoaded && !CoroutineManager.IsCoroutineRunning(loadingCoroutine))
|
||||
{
|
||||
throw new LoadingException(loadingCoroutine.Exception);
|
||||
@@ -915,6 +866,11 @@ namespace Barotrauma
|
||||
{
|
||||
gameSession.ToggleTabMenu();
|
||||
}
|
||||
else if (GUIMessageBox.VisibleBox as GUIMessageBox != null &&
|
||||
GUIMessageBox.VisibleBox.UserData as string == "bugreporter")
|
||||
{
|
||||
((GUIMessageBox)GUIMessageBox.VisibleBox).Close();
|
||||
}
|
||||
else if (GUI.PauseMenuOpen)
|
||||
{
|
||||
GUI.TogglePauseMenu();
|
||||
@@ -999,11 +955,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
NetworkMember?.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);
|
||||
|
||||
SteamManager.Update((float)Timing.Step);
|
||||
@@ -1123,6 +1079,10 @@ namespace Barotrauma
|
||||
|
||||
if (GameSession != null)
|
||||
{
|
||||
double roundDuration = Timing.TotalTime - GameSession.RoundStartTime;
|
||||
GameAnalyticsManager.AddProgressionEvent(GameAnalyticsManager.ProgressionStatus.Fail,
|
||||
GameSession.GameMode?.Name ?? "none",
|
||||
roundDuration);
|
||||
if (Tutorial.Initialized)
|
||||
{
|
||||
((TutorialMode)GameSession.GameMode).Tutorial?.Stop();
|
||||
@@ -1131,6 +1091,7 @@ namespace Barotrauma
|
||||
GUIMessageBox.CloseAll();
|
||||
MainMenuScreen.Select();
|
||||
GameSession = null;
|
||||
|
||||
}
|
||||
|
||||
public void ShowCampaignDisclaimer(Action onContinue = null)
|
||||
@@ -1254,7 +1215,7 @@ namespace Barotrauma
|
||||
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(); }
|
||||
|
||||
base.OnExiting(sender, args);
|
||||
|
||||
@@ -330,7 +330,7 @@ namespace Barotrauma
|
||||
if (data == null) { return false; }
|
||||
if (GameMain.NetworkMember?.ConnectedClients?.Find(c => c.Character == data) is Client client)
|
||||
{
|
||||
CreateModerationContextMenu(PlayerInput.MousePosition.ToPoint(), client);
|
||||
NetLobbyScreen.CreateModerationContextMenu(client);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -789,7 +789,7 @@ namespace Barotrauma
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -968,7 +968,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (!CanIssueOrders) { return false; }
|
||||
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;
|
||||
},
|
||||
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()
|
||||
{
|
||||
if (GUI.DisableHUD) { return; }
|
||||
@@ -1902,7 +1804,7 @@ namespace Barotrauma
|
||||
private Hull hullContext;
|
||||
private WallSection wallContext;
|
||||
private bool isContextual;
|
||||
private readonly List<Order> contextualOrders = new List<Order>();
|
||||
private readonly List<OrderInfo> contextualOrders = new List<OrderInfo>();
|
||||
private Point shorcutCenterNodeOffset;
|
||||
private const int maxShortcutNodeCount = 4;
|
||||
|
||||
@@ -2605,7 +2507,7 @@ namespace Barotrauma
|
||||
{
|
||||
order = orders[i];
|
||||
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());
|
||||
optionNodes.Add(new Tuple<GUIComponent, Keys>(
|
||||
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), order, (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false),
|
||||
@@ -2621,7 +2523,6 @@ namespace Barotrauma
|
||||
if (contextualOrders.None())
|
||||
{
|
||||
string orderIdentifier;
|
||||
|
||||
// Check if targeting an item or a hull
|
||||
if (itemContext != null && itemContext.IsPlayerTeamInteractable)
|
||||
{
|
||||
@@ -2630,84 +2531,89 @@ namespace Barotrauma
|
||||
{
|
||||
targetComponent = null;
|
||||
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))) ||
|
||||
p.TryGetTargetItemComponent(itemContext, out targetComponent))
|
||||
if (p.HasOptionSpecificTargetItems)
|
||||
{
|
||||
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
|
||||
orderIdentifier = "operateweapons";
|
||||
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)) ??
|
||||
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems));
|
||||
if (turret != null) { contextualOrders.Add(new Order(operateWeaponsPrefab, turret.Item, turret, Character.Controlled)); }
|
||||
var turret = itemContext.GetConnectedComponents<Turret>().FirstOrDefault(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item)) ??
|
||||
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item));
|
||||
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
|
||||
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"))))
|
||||
{
|
||||
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"))))
|
||||
{
|
||||
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
|
||||
{
|
||||
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)
|
||||
orderIdentifier = "pumpwater";
|
||||
if (contextualOrders.FirstOrDefault(o => o.Identifier.Equals(orderIdentifier)) is Order o &&
|
||||
itemContext.Components.FirstOrDefault(c => c.GetType() == o.ItemComponentType) is Pump pump)
|
||||
if (contextualOrders.FirstOrDefault(info => info.Order.Identifier.Equals(orderIdentifier)) is OrderInfo pumpOrderInfo && pumpOrderInfo.Order is Order pumpOrder &&
|
||||
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())
|
||||
{
|
||||
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))
|
||||
{
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
AddIgnoreOrder(wallContext);
|
||||
}
|
||||
}
|
||||
|
||||
void AddIgnoreOrder(IIgnorable target)
|
||||
{
|
||||
var orderIdentifier = "ignorethis";
|
||||
if (!target.OrderedToBeIgnored && contextualOrders.None(o => o.Identifier == orderIdentifier))
|
||||
if (!target.OrderedToBeIgnored && contextualOrders.None(info => info.Order.Identifier == orderIdentifier))
|
||||
{
|
||||
AddOrder();
|
||||
}
|
||||
else
|
||||
{
|
||||
orderIdentifier = "unignorethis";
|
||||
if (target.OrderedToBeIgnored && contextualOrders.None(o => o.Identifier == orderIdentifier))
|
||||
if (target.OrderedToBeIgnored && contextualOrders.None(info => info.Order.Identifier == orderIdentifier))
|
||||
{
|
||||
AddOrder();
|
||||
}
|
||||
@@ -2717,64 +2623,61 @@ namespace Barotrauma
|
||||
{
|
||||
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
|
||||
{
|
||||
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";
|
||||
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);
|
||||
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(o => o.Category != OrderCategory.Movement) && characters.Any(c => c != Character.Controlled))
|
||||
if (contextualOrders.None(info => info.Order.Category != OrderCategory.Movement) && characters.Any(c => c != Character.Controlled))
|
||||
{
|
||||
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
|
||||
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));
|
||||
bool disableNode = !CanCharacterBeHeard();
|
||||
for (int i = 0; i < contextualOrders.Count; i++)
|
||||
{
|
||||
optionNodes.Add(new Tuple<GUIComponent, Keys>(
|
||||
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), contextualOrders[i], (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false),
|
||||
!disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None));
|
||||
var info = contextualOrders[i];
|
||||
int hotkey = (i + 1) % 10;
|
||||
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
|
||||
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 => item.HasTag(o.TargetItems))) { return true; }
|
||||
if (Order.PrefabList.Any(o => o.TargetItemsMatchItem(item))) { 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.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; }
|
||||
var operateWeaponsPrefab = Order.GetPrefab("operateweapons");
|
||||
return item.Components.Any(c => c is Controller) &&
|
||||
(item.GetConnectedComponents<Turret>().Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)) ||
|
||||
item.GetConnectedComponents<Turret>(recursive: true).Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)));
|
||||
return Order.GetPrefab("operateweapons") is Order operateWeaponsPrefab && item.Components.Any(c => c is Controller) &&
|
||||
(item.GetConnectedComponents<Turret>().Any(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item)) ||
|
||||
item.GetConnectedComponents<Turret>(recursive: true).Any(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item)));
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
return true;
|
||||
@@ -2943,7 +2847,9 @@ namespace Barotrauma
|
||||
}
|
||||
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();
|
||||
}
|
||||
return true;
|
||||
@@ -3013,7 +2919,9 @@ namespace Barotrauma
|
||||
}
|
||||
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();
|
||||
}
|
||||
return true;
|
||||
@@ -3218,7 +3126,9 @@ namespace Barotrauma
|
||||
OnClicked = (_, userData) =>
|
||||
{
|
||||
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();
|
||||
return true;
|
||||
}
|
||||
@@ -3496,6 +3406,11 @@ namespace Barotrauma
|
||||
return order.Name;
|
||||
}
|
||||
|
||||
private int GetManualOrderPriority(Character character, Order order)
|
||||
{
|
||||
return character?.Info?.GetManualOrderPriority(order) ?? CharacterInfo.HighestManualOrderPriority;
|
||||
}
|
||||
|
||||
#region Crew Member Assignment Logic
|
||||
private bool CanOpenManualAssignment(GUIComponent node)
|
||||
{
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Barotrauma
|
||||
|
||||
public override void ShowStartMessage()
|
||||
{
|
||||
foreach (Mission mission in Missions)
|
||||
foreach (Mission mission in Missions.ToList())
|
||||
{
|
||||
new GUIMessageBox(
|
||||
mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name,
|
||||
|
||||
@@ -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 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),
|
||||
TextManager.Get("NewCampaign"), style: "GUITabButton")
|
||||
|
||||
@@ -475,12 +475,12 @@ namespace Barotrauma
|
||||
ItemComponent targetItem = null;
|
||||
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; }
|
||||
targetItem = orderPrefab.GetTargetItemComponent(targetEntity);
|
||||
}
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -635,9 +635,9 @@ namespace Barotrauma
|
||||
}
|
||||
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).";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("RoundSummary:InvalidCauseOfDeath", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
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.Replace("[name]", characterInfo.Name));
|
||||
GameAnalyticsManager.AddErrorEventOnce("RoundSummary:InvalidCauseOfDeath", GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", characterInfo.SpeciesName));
|
||||
statusText = TextManager.Get("CauseOfDeathDescription.Unknown");
|
||||
statusColor = GUI.Style.Red;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using OpenAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -365,6 +363,12 @@ namespace Barotrauma
|
||||
TextManager.Get("Settings"), textAlignment: Alignment.TopLeft, font: GUI.LargeFont)
|
||||
{ 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);
|
||||
|
||||
var corePackageDropdown = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform))
|
||||
@@ -509,17 +513,53 @@ namespace Barotrauma
|
||||
ApplySettings();
|
||||
GameMain.Instance.Exit();
|
||||
return true;
|
||||
}; msgBox.Buttons[1].OnClicked += (btn, userdata) =>
|
||||
};
|
||||
msgBox.Buttons[1].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
Language = prevLanguage;
|
||||
languageDD.SelectItem(Language);
|
||||
msgBox.Close();
|
||||
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 --------------------------------------
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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 --------------------------------------------------------------
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -406,7 +406,7 @@ namespace Barotrauma.Items.Components
|
||||
DebugConsole.Log("Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"ItemComponent.PlaySound:" + item.Name + GetType().ToString(),
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -11,7 +8,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
public float BackgroundSpriteDepth
|
||||
{
|
||||
get { return item.GetDrawDepth() + 0.1f; }
|
||||
get { return item.GetDrawDepth() + 0.05f; }
|
||||
}
|
||||
|
||||
public Vector2 DrawSize
|
||||
|
||||
@@ -672,15 +672,13 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentMode == MiniMapMode.HullStatus)
|
||||
if (currentMode == MiniMapMode.HullStatus && item.Submarine != null)
|
||||
{
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
spriteBatch.End();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
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)
|
||||
@@ -690,7 +688,7 @@ namespace Barotrauma.Items.Components
|
||||
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; }
|
||||
if (gap.IsRoomToRoom || gap.linkedTo.Count == 0 || 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;
|
||||
@@ -701,7 +699,6 @@ namespace Barotrauma.Items.Components
|
||||
color, origin: sprite.Origin, rotate: 0.0f, scale: scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentMode == MiniMapMode.HullStatus && hullStatusComponents != null)
|
||||
{
|
||||
@@ -991,8 +988,40 @@ namespace Barotrauma.Items.Components
|
||||
continue;
|
||||
}
|
||||
|
||||
hullData.HullOxygenAmount = RequireOxygenDetectors ? hullData.ReceivedOxygenAmount : hull.OxygenPercentage;
|
||||
hullData.HullWaterAmount = RequireWaterDetectors ? hullData.ReceivedWaterAmount : Math.Min(hull.WaterVolume / hull.Volume, 1.0f);
|
||||
if (RequireOxygenDetectors)
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -1013,15 +1042,6 @@ namespace Barotrauma.Items.Components
|
||||
float? oxygenAmount = hullData.HullOxygenAmount,
|
||||
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;
|
||||
Color line1Color = GUI.Style.Red;
|
||||
|
||||
@@ -1111,10 +1131,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void DrawHUDBack(SpriteBatch spriteBatch, GUICustomComponent container)
|
||||
{
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
if (item.Submarine == null) { return; }
|
||||
|
||||
DrawSubmarine(spriteBatch);
|
||||
}
|
||||
|
||||
if (Voltage < MinVoltage) { return; }
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
@@ -1155,7 +1174,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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);
|
||||
|
||||
@@ -1418,7 +1438,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (linkedEntity is Hull linkedHull)
|
||||
{
|
||||
if (linkedHulls.Contains(linkedHull)) { continue; }
|
||||
if (linkedHulls.Contains(linkedHull) || linkedHull.HiddenInGame) { continue; }
|
||||
linkedHulls.Add(linkedHull);
|
||||
GetLinkedHulls(linkedHull, linkedHulls);
|
||||
}
|
||||
@@ -1598,7 +1618,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1623,6 +1623,11 @@ namespace Barotrauma.Items.Components
|
||||
markerDistances.Add(targetIdentifier, cachedDistance);
|
||||
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;
|
||||
|
||||
@@ -35,6 +35,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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;
|
||||
|
||||
[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 (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; }
|
||||
|
||||
float maxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(character);
|
||||
float defaultMaxCondition = item.MaxCondition / maxRepairConditionMultiplier;
|
||||
float defaultMaxCondition = item.MaxCondition / item.MaxRepairConditionMultiplier;
|
||||
|
||||
if (MathUtils.Percentage(item.Condition, defaultMaxCondition) < RepairThreshold) { return true; }
|
||||
|
||||
if (CurrentFixer == character)
|
||||
{
|
||||
float condition = item.Condition / item.MaxRepairConditionMultiplier;
|
||||
float maxCondition = item.MaxCondition / item.MaxRepairConditionMultiplier;
|
||||
if (condition < maxCondition * maxRepairConditionMultiplier)
|
||||
if (item.Condition < item.MaxCondition)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -145,11 +149,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
progressBar = new GUIProgressBar(new RectTransform(new Vector2(0.6f, 1.0f), progressBarHolder.RectTransform),
|
||||
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)
|
||||
{
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
qteTimer = qteTime;
|
||||
|
||||
repairButtonText = TextManager.Get("RepairButton");
|
||||
repairingText = TextManager.Get("Repairing");
|
||||
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;
|
||||
item.CreateClientEvent(this);
|
||||
return true;
|
||||
},
|
||||
OnButtonDown = () =>
|
||||
{
|
||||
QTEAction();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
RepairButton.TextBlock.AutoScaleHorizontal = true;
|
||||
@@ -183,6 +195,11 @@ namespace Barotrauma.Items.Components
|
||||
requestStartFixAction = FixActions.Sabotage;
|
||||
item.CreateClientEvent(this);
|
||||
return true;
|
||||
},
|
||||
OnButtonDown = () =>
|
||||
{
|
||||
QTEAction();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -253,6 +270,21 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
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
|
||||
{
|
||||
@@ -270,6 +302,26 @@ namespace Barotrauma.Items.Components
|
||||
progressBar.BarSize = item.Condition / defaultMaxCondition;
|
||||
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)
|
||||
{
|
||||
float extraCondition = item.MaxCondition * (item.MaxRepairConditionMultiplier - 1.0f);
|
||||
@@ -282,7 +334,7 @@ namespace Barotrauma.Items.Components
|
||||
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) ?
|
||||
repairButtonText :
|
||||
repairingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
|
||||
@@ -352,6 +404,29 @@ namespace Barotrauma.Items.Components
|
||||
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)
|
||||
{
|
||||
deteriorationTimer = msg.ReadSingle();
|
||||
@@ -363,11 +438,13 @@ namespace Barotrauma.Items.Components
|
||||
currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
|
||||
CurrentFixer = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null;
|
||||
item.MaxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(CurrentFixer);
|
||||
repairBoost = msg.ReadSingle();
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
{
|
||||
msg.WriteRangedInteger((int)requestStartFixAction, 0, 2);
|
||||
msg.Write(qteSuccess);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,7 @@ namespace Barotrauma.Items.Components
|
||||
AutoHideScrollBar = false
|
||||
};
|
||||
|
||||
// Create fillerBlock to cover historyBox so new values appear at the bottom of historyBox
|
||||
// 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
|
||||
};
|
||||
CreateFillerBlock();
|
||||
|
||||
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)
|
||||
{
|
||||
if (input.Length > MaxMessageLength)
|
||||
@@ -130,7 +135,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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)
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -1445,7 +1445,7 @@ namespace Barotrauma
|
||||
#else
|
||||
if (GameSettings.VerboseLogging) { DebugConsole.ThrowError(errorMsg); }
|
||||
#endif
|
||||
GameAnalyticsManager.AddErrorEventOnce("Item.ClientReadPosition:nophysicsbody", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Item.ClientReadPosition:nophysicsbody", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1586,7 +1586,7 @@ namespace Barotrauma
|
||||
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));
|
||||
GameAnalyticsManager.AddErrorEventOnce("Item.ReadSpawnData:PrefabNotFound" + (itemName ?? "null") + (itemIdentifier ?? "null"),
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Critical,
|
||||
GameAnalyticsManager.ErrorSeverity.Critical,
|
||||
errorMsg);
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
return null;
|
||||
@@ -1607,7 +1607,7 @@ namespace Barotrauma
|
||||
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}.";
|
||||
GameAnalyticsManager.AddErrorEventOnce("Item.ReadSpawnData:ContainerIndexOutOfRange" + (itemName ?? "null") + (itemIdentifier ?? "null"),
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
errorMsg);
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
inventory = parentItem.GetComponent<ItemContainer>()?.Inventory;
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace Barotrauma
|
||||
private IEnumerable<CoroutineStatus> DimLight(LightSource light)
|
||||
{
|
||||
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));
|
||||
currBrightness -= 1.0f / flashDuration * CoroutineManager.DeltaTime;
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError("Invalid left normal");
|
||||
#endif
|
||||
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 + ")");
|
||||
|
||||
if (cell.Body != null)
|
||||
@@ -127,7 +127,7 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError("Invalid right normal");
|
||||
#endif
|
||||
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 + ")");
|
||||
|
||||
if (cell.Body != null)
|
||||
|
||||
@@ -497,11 +497,13 @@ namespace Barotrauma.Lights
|
||||
return true;
|
||||
}
|
||||
|
||||
private readonly Dictionary<Hull, Rectangle> visibleHulls = new Dictionary<Hull, Rectangle>();
|
||||
private Dictionary<Hull, Rectangle> GetVisibleHulls(Camera cam)
|
||||
{
|
||||
Dictionary<Hull, Rectangle> visibleHulls = new Dictionary<Hull, Rectangle>();
|
||||
visibleHulls.Clear();
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.HiddenInGame) { continue; }
|
||||
var drawRect =
|
||||
hull.Submarine == null ?
|
||||
hull.Rect :
|
||||
|
||||
@@ -536,7 +536,7 @@ namespace Barotrauma
|
||||
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})";
|
||||
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++)
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Error when loading round sound (" + element + ") - file path not set";
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Failed to load sound file \"" + filename + "\".";
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -148,7 +148,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Failed to load sound file \"" + roundSound.Filename + "\".";
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -183,13 +183,14 @@ namespace Barotrauma
|
||||
visibleSubs.Clear();
|
||||
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(
|
||||
sub.Borders.X + (int)sub.WorldPosition.X - 500,
|
||||
sub.Borders.Y + (int)sub.WorldPosition.Y + 500,
|
||||
sub.Borders.Width + 1000,
|
||||
sub.Borders.Height + 1000);
|
||||
sub.VisibleBorders.X + (int)sub.WorldPosition.X - margin,
|
||||
sub.VisibleBorders.Y + (int)sub.WorldPosition.Y + margin,
|
||||
sub.VisibleBorders.Width + margin * 2,
|
||||
sub.VisibleBorders.Height + margin * 2);
|
||||
|
||||
if (RectsOverlap(worldBorders, cam.WorldView))
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Barotrauma
|
||||
catch (Exception 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.");
|
||||
PreviewImage = null;
|
||||
}
|
||||
|
||||
@@ -649,7 +649,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
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);
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", new string[2] { "[message]", "[targetsite]" }, new string[2] { e.Message, e.TargetSite.ToString() }));
|
||||
Disconnect();
|
||||
@@ -779,7 +779,7 @@ namespace Barotrauma.Networking
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Error while reading an ingame update message from server.", e);
|
||||
#endif
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadDataMessage:ReadIngameUpdate", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadDataMessage:ReadIngameUpdate", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw;
|
||||
}
|
||||
break;
|
||||
@@ -791,7 +791,7 @@ namespace Barotrauma.Networking
|
||||
errorMsg += "\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"GameClient.ReadDataMessage:VoipClientNull",
|
||||
GameMain.Client == null ? GameAnalyticsSDK.Net.EGAErrorSeverity.Error : GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
|
||||
GameMain.Client == null ? GameAnalyticsManager.ErrorSeverity.Error : GameAnalyticsManager.ErrorSeverity.Warning,
|
||||
errorMsg);
|
||||
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." +
|
||||
" 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);
|
||||
}
|
||||
|
||||
@@ -1010,7 +1010,7 @@ namespace Barotrauma.Networking
|
||||
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))})";
|
||||
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);
|
||||
}
|
||||
GameMain.GameSession.EnforceMissionOrder(serverMissionIdentifiers);
|
||||
@@ -1031,7 +1031,7 @@ namespace Barotrauma.Networking
|
||||
", seed: " + Level.Loaded.Seed +
|
||||
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" +
|
||||
", 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);
|
||||
}
|
||||
else
|
||||
@@ -1047,7 +1047,7 @@ namespace Barotrauma.Networking
|
||||
", seed: " + Level.Loaded.Seed +
|
||||
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" +
|
||||
", 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);
|
||||
}
|
||||
}
|
||||
@@ -1117,8 +1117,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"GameClient.HandleDisconnectMessage",
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Debug,
|
||||
"Client received a disconnect message. Reason: " + disconnectReason.ToString() + ", message: " + disconnectMsg);
|
||||
GameAnalyticsManager.ErrorSeverity.Debug,
|
||||
"Client received a disconnect message. Reason: " + disconnectReason.ToString());
|
||||
}
|
||||
|
||||
if (disconnectReason == DisconnectReason.ServerFull)
|
||||
@@ -1532,7 +1532,7 @@ namespace Barotrauma.Networking
|
||||
gameStarted = true;
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
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;
|
||||
yield return CoroutineStatus.Failure;
|
||||
}
|
||||
@@ -1544,7 +1544,7 @@ namespace Barotrauma.Networking
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
string errorMsg = "Failed to select shuttle \"" + shuttleName + "\" (hash: " + shuttleHash + ").";
|
||||
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;
|
||||
yield return CoroutineStatus.Failure;
|
||||
}
|
||||
@@ -2116,9 +2116,6 @@ namespace Barotrauma.Networking
|
||||
bool autoRestartEnabled = inc.ReadBoolean();
|
||||
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
|
||||
//or if we're still waiting for the initial update
|
||||
if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) &&
|
||||
@@ -2170,20 +2167,13 @@ namespace Barotrauma.Networking
|
||||
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.LevelSeed = levelSeed;
|
||||
GameMain.NetLobbyScreen.SetLevelDifficulty(levelDifficulty);
|
||||
GameMain.NetLobbyScreen.SetRadiationEnabled(radiationEnabled);
|
||||
GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode);
|
||||
GameMain.NetLobbyScreen.SetBotCount(botCount);
|
||||
GameMain.NetLobbyScreen.SetMaxMissionCount(maxMissionCount);
|
||||
GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer);
|
||||
|
||||
serverSettings.VoiceChatEnabled = voiceChatEnabled;
|
||||
@@ -2355,7 +2345,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
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");
|
||||
|
||||
@@ -2591,29 +2581,29 @@ namespace Barotrauma.Networking
|
||||
subElement.ToolTip = newSub.Description;
|
||||
}
|
||||
|
||||
if (GameMain.NetLobbyScreen.FailedSelectedSub != null &&
|
||||
GameMain.NetLobbyScreen.FailedSelectedSub.First == newSub.Name &&
|
||||
GameMain.NetLobbyScreen.FailedSelectedSub.Second == newSub.MD5Hash.Hash)
|
||||
if (GameMain.NetLobbyScreen.FailedSelectedSub.HasValue &&
|
||||
GameMain.NetLobbyScreen.FailedSelectedSub.Value.Name == newSub.Name &&
|
||||
GameMain.NetLobbyScreen.FailedSelectedSub.Value.Hash == newSub.MD5Hash.Hash)
|
||||
{
|
||||
GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.Hash, GameMain.NetLobbyScreen.SubList);
|
||||
}
|
||||
|
||||
if (GameMain.NetLobbyScreen.FailedSelectedShuttle != null &&
|
||||
GameMain.NetLobbyScreen.FailedSelectedShuttle.First == newSub.Name &&
|
||||
GameMain.NetLobbyScreen.FailedSelectedShuttle.Second == newSub.MD5Hash.Hash)
|
||||
if (GameMain.NetLobbyScreen.FailedSelectedShuttle.HasValue &&
|
||||
GameMain.NetLobbyScreen.FailedSelectedShuttle.Value.Name == newSub.Name &&
|
||||
GameMain.NetLobbyScreen.FailedSelectedShuttle.Value.Hash == newSub.MD5Hash.Hash)
|
||||
{
|
||||
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);
|
||||
if (failedCampaignSub != null)
|
||||
NetLobbyScreen.FailedSubInfo failedCampaignSub = GameMain.NetLobbyScreen.FailedCampaignSubs.Find(s => s.Name == newSub.Name && s.Hash == newSub.MD5Hash.Hash);
|
||||
if (failedCampaignSub != default)
|
||||
{
|
||||
GameMain.NetLobbyScreen.CampaignSubmarines.Add(newSub);
|
||||
GameMain.NetLobbyScreen.FailedCampaignSubs.Remove(failedCampaignSub);
|
||||
}
|
||||
|
||||
Pair<string, string> failedOwnedSub = GameMain.NetLobbyScreen.FailedOwnedSubs.Find(s => s.First == newSub.Name && s.Second == newSub.MD5Hash.Hash);
|
||||
if (failedOwnedSub != null)
|
||||
NetLobbyScreen.FailedSubInfo failedOwnedSub = GameMain.NetLobbyScreen.FailedOwnedSubs.Find(s => s.Name == newSub.Name && s.Hash == newSub.MD5Hash.Hash);
|
||||
if (failedOwnedSub != default)
|
||||
{
|
||||
GameMain.NetLobbyScreen.ServerOwnedSubmarines.Add(newSub);
|
||||
GameMain.NetLobbyScreen.FailedOwnedSubs.Remove(failedOwnedSub);
|
||||
@@ -2946,7 +2936,6 @@ namespace Barotrauma.Networking
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.SelectSub);
|
||||
msg.Write(false);
|
||||
msg.Write(isShuttle); msg.WritePadBits();
|
||||
msg.Write((UInt16)subIndex);
|
||||
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
@@ -2954,23 +2943,6 @@ namespace Barotrauma.Networking
|
||||
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>
|
||||
/// Tell the server to select a mode (permission required)
|
||||
/// </summary>
|
||||
|
||||
@@ -233,7 +233,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
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
|
||||
//msg.BitPosition = (int)(msgPosition + msgLength * 8);
|
||||
@@ -252,7 +252,7 @@ namespace Barotrauma.Networking
|
||||
DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
|
||||
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
msg.BitPosition = (int)(msgPosition + msgLength * 8);
|
||||
msg.ReadPadBits();
|
||||
}
|
||||
|
||||
@@ -115,13 +115,11 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
byte incByte = inc.ReadByte();
|
||||
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
|
||||
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
|
||||
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
|
||||
|
||||
//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));
|
||||
}
|
||||
@@ -133,7 +131,7 @@ namespace Barotrauma.Networking
|
||||
initializationStep = ConnectionInitialization.Success;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,33 +112,28 @@ namespace Barotrauma.Networking
|
||||
NetworkConnection.TimeoutThresholdInGame :
|
||||
NetworkConnection.TimeoutThreshold;
|
||||
|
||||
byte incByte = 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;
|
||||
PacketHeader packetHeader = (PacketHeader)data[0];
|
||||
|
||||
if (!isServerMessage) { return; }
|
||||
if (!packetHeader.IsServerMessage()) { return; }
|
||||
|
||||
if (isConnectionInitializationStep)
|
||||
if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
ulong low = Lidgren.Network.NetBitWriter.ReadUInt32(data, 32, 8);
|
||||
ulong high = Lidgren.Network.NetBitWriter.ReadUInt32(data, 32, 8 + 32);
|
||||
ulong lobbyId = low + (high << 32);
|
||||
|
||||
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)
|
||||
{
|
||||
incomingInitializationMessages.Add(inc);
|
||||
}
|
||||
}
|
||||
else if (isHeartbeatMessage)
|
||||
else if (packetHeader.IsHeartbeatMessage())
|
||||
{
|
||||
return; //TODO: implement heartbeats
|
||||
}
|
||||
else if (isDisconnectMessage)
|
||||
else if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
IReadMessage inc = new ReadOnlyMessage(data, false, 1, dataLength - 1, ServerConnection);
|
||||
string msg = inc.ReadString();
|
||||
@@ -147,10 +142,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt16 length = data[1];
|
||||
length |= (UInt16)(((UInt32)data[2]) << 8);
|
||||
UInt16 length = Lidgren.Network.NetBitWriter.ReadUInt16(data, 16, 8);
|
||||
|
||||
IReadMessage inc = new ReadOnlyMessage(data, isCompressed, 3, length, ServerConnection);
|
||||
IReadMessage inc = new ReadOnlyMessage(data, packetHeader.IsCompressed(), 3, length, ServerConnection);
|
||||
incomingDataMessages.Add(inc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,18 +147,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
DeliveryMethod deliveryMethod = (DeliveryMethod)data[0];
|
||||
|
||||
byte incByte = 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;
|
||||
PacketHeader packetHeader = (PacketHeader)data[1];
|
||||
|
||||
if (!remotePeer.Authenticated & !remotePeer.Authenticating && isConnectionInitializationStep)
|
||||
if (!remotePeer.Authenticated & !remotePeer.Authenticating && packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
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();
|
||||
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
|
||||
{
|
||||
@@ -242,17 +237,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
int p2pDataStart = inc.BytePosition;
|
||||
|
||||
byte incByte = 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;
|
||||
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
|
||||
|
||||
if (recipientSteamId != selfSteamID)
|
||||
{
|
||||
if (!isServerMessage)
|
||||
if (!packetHeader.IsServerMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received non-server message meant for remote peer");
|
||||
return;
|
||||
@@ -262,7 +251,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (peer == null) { return; }
|
||||
|
||||
if (isDisconnectMessage)
|
||||
if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
DisconnectPeer(peer, inc.ReadString());
|
||||
return;
|
||||
@@ -273,8 +262,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
case DeliveryMethod.Reliable:
|
||||
case DeliveryMethod.ReliableOrdered:
|
||||
//the documentation seems to suggest that the Reliable send type
|
||||
//enforces packet order (TODO: verify)
|
||||
//the documentation seems to suggest that the
|
||||
//Reliable send type enforces packet order
|
||||
sendType = Steamworks.P2PSend.Reliable;
|
||||
break;
|
||||
default:
|
||||
@@ -284,17 +273,31 @@ namespace Barotrauma.Networking
|
||||
|
||||
byte[] p2pData;
|
||||
|
||||
if (isConnectionInitializationStep)
|
||||
if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
p2pData = new byte[inc.LengthBytes - p2pDataStart + 8];
|
||||
p2pData[0] = inc.Buffer[p2pDataStart];
|
||||
Lidgren.Network.NetBitWriter.WriteUInt64(SteamManager.CurrentLobbyID, 64, p2pData, 8);
|
||||
Array.Copy(inc.Buffer, p2pDataStart+1, p2pData, 9, inc.LengthBytes - p2pDataStart - 1);
|
||||
Lidgren.Network.NetBitWriter.WriteUInt64(SteamManager.CurrentLobbyID, 8 * 8, p2pData, 1 * 8);
|
||||
Array.Copy(inc.Buffer, p2pDataStart+1, p2pData, 1 + 8, inc.LengthBytes - p2pDataStart - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
p2pData = new byte[inc.LengthBytes - p2pDataStart];
|
||||
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)
|
||||
@@ -323,21 +326,21 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isDisconnectMessage)
|
||||
if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received disconnect message from owned server");
|
||||
return;
|
||||
}
|
||||
if (!isServerMessage)
|
||||
if (!packetHeader.IsServerMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received non-server message from owned server");
|
||||
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();
|
||||
outMsg.Write(selfSteamID);
|
||||
@@ -358,7 +361,7 @@ namespace Barotrauma.Networking
|
||||
initializationStep = ConnectionInitialization.Success;
|
||||
}
|
||||
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);
|
||||
|
||||
return;
|
||||
|
||||
@@ -242,7 +242,8 @@ namespace Barotrauma.Networking
|
||||
textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea()
|
||||
{
|
||||
Data = data,
|
||||
OnClick = GameMain.NetLobbyScreen.SelectPlayer
|
||||
OnClick = GameMain.NetLobbyScreen.SelectPlayer,
|
||||
OnSecondaryClick = GameMain.NetLobbyScreen.ShowPlayerContextMenu
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
@@ -128,8 +129,18 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
cachedServerListInfo = null;
|
||||
|
||||
NetFlags requiredFlags = (NetFlags)incMsg.ReadByte();
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.Name))
|
||||
{
|
||||
ServerName = incMsg.ReadString();
|
||||
}
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.Message))
|
||||
{
|
||||
ServerMessageText = incMsg.ReadString();
|
||||
}
|
||||
PlayStyle = (PlayStyle)incMsg.ReadByte();
|
||||
MaxPlayers = incMsg.ReadByte();
|
||||
HasPassword = incMsg.ReadBoolean();
|
||||
IsPublic = incMsg.ReadBoolean();
|
||||
@@ -139,23 +150,23 @@ namespace Barotrauma.Networking
|
||||
TickRate = incMsg.ReadRangedInteger(1, 60);
|
||||
GameMain.NetworkMember.TickRate = TickRate;
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.Properties))
|
||||
{
|
||||
ReadExtraCargo(incMsg);
|
||||
}
|
||||
|
||||
ReadHiddenSubs(incMsg);
|
||||
|
||||
GameMain.NetLobbyScreen.UpdateSubVisibility();
|
||||
|
||||
Voting.ClientRead(incMsg);
|
||||
|
||||
bool isAdmin = incMsg.ReadBoolean();
|
||||
incMsg.ReadPadBits();
|
||||
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;
|
||||
|
||||
@@ -225,8 +236,6 @@ namespace Barotrauma.Networking
|
||||
|
||||
outMsg.Write(autoRestart != null);
|
||||
outMsg.Write(autoRestart ?? false);
|
||||
outMsg.Write(radiationEnabled ?? RadiationEnabled);
|
||||
outMsg.Write((byte)maxMissionCount + 1);
|
||||
|
||||
outMsg.WritePadBits();
|
||||
}
|
||||
@@ -711,7 +720,6 @@ namespace Barotrauma.Networking
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
|
||||
if (ip.InventoryIcon != null || ip.sprite != null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
MinValueInt = 0,
|
||||
MaxValueInt = 100,
|
||||
MaxValueInt = MaxExtraCargoItemsOfType,
|
||||
IntValue = cargoVal
|
||||
};
|
||||
amountInput.OnValueChanged += (numberInput) =>
|
||||
@@ -742,16 +750,26 @@ namespace Barotrauma.Networking
|
||||
if (ExtraCargo.ContainsKey(ip))
|
||||
{
|
||||
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);
|
||||
}
|
||||
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
|
||||
//--------------------------------------------------------------------------------
|
||||
@@ -913,6 +931,7 @@ namespace Barotrauma.Networking
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
Whitelist.CreateWhiteListFrame(settingsTabs[(int)SettingsTab.Whitelist]);
|
||||
Whitelist.localEnabled = Whitelist.Enabled;
|
||||
}
|
||||
|
||||
private void CreateLabeledSlider(GUIComponent parent, string labelTag, out GUIScrollBar slider, out GUITextBlock label)
|
||||
|
||||
@@ -147,7 +147,14 @@ namespace Barotrauma.Steam
|
||||
|
||||
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;
|
||||
return;
|
||||
}
|
||||
@@ -525,18 +532,6 @@ namespace Barotrauma.Steam
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
@@ -884,9 +879,9 @@ namespace Barotrauma.Steam
|
||||
|
||||
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,
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + e.Message);
|
||||
GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + e.Message);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1434,8 +1429,8 @@ namespace Barotrauma.Steam
|
||||
"\" not found. Could not combine path (" + (item.Directory ?? "directory name empty") + ").";
|
||||
DebugConsole.ThrowError(errorMessage);
|
||||
GameAnalyticsManager.AddErrorEventOnce("SteamManager.CheckWorkshopItemInstalled:PathCombineException" + item.Title,
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
errorMessage);
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Metadata file for a Workshop item not found. Could not combine path.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1567,8 +1562,8 @@ namespace Barotrauma.Steam
|
||||
}
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"SteamManager.AutoUpdateWorkshopItems:" + e.Message,
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"Failed to autoupdate workshop item \"" + item.Title + "\". " + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Failed to autoupdate workshop item. " + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Barotrauma.Networking
|
||||
UserData = "capturedevicenotfound"
|
||||
};
|
||||
}
|
||||
GameAnalyticsManager.AddErrorEventOnce("Alc.CaptureDeviceOpenFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
GameAnalyticsManager.AddErrorEventOnce("Alc.CaptureDeviceOpenFailed", GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Alc.CaptureDeviceOpen(" + deviceName + ") failed. Error code: " + errorCode);
|
||||
GameMain.Config.VoiceSetting = GameSettings.VoiceMode.Disabled;
|
||||
Instance?.Dispose();
|
||||
|
||||
@@ -56,17 +56,12 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
nameBox.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;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
localEnabled = Enabled;
|
||||
|
||||
var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), whitelistFrame.RectTransform));
|
||||
foreach (WhiteListedPlayer wlp in whitelistedPlayers)
|
||||
{
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace Barotrauma.Particles
|
||||
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; }
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace Barotrauma.Particles
|
||||
for (float z = 0.0f; z < dist; z += Prefab.Properties.EmitAcrossRayInterval)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
if (particlePrefab == null) { return; }
|
||||
@@ -191,7 +191,7 @@ namespace Barotrauma.Particles
|
||||
Vector2 velocity = Vector2.Zero;
|
||||
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));
|
||||
velocity = dir * Rand.Range(Prefab.Properties.VelocityMin, Prefab.Properties.VelocityMax) * velocityMultiplier;
|
||||
position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax);
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#endif
|
||||
GameAnalyticsManager.AddErrorEventOnce("PhysicsBody.ClientRead:InvalidData" + parentDebugName,
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
errorMsg);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#region Using Statements
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GameAnalyticsSDK.Net;
|
||||
using Barotrauma.Steam;
|
||||
using System.Diagnostics;
|
||||
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:");
|
||||
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.SendUserStatistics)
|
||||
if (GameAnalyticsManager.SendUserStatistics)
|
||||
{
|
||||
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
|
||||
{
|
||||
|
||||
@@ -7,14 +7,10 @@ namespace Barotrauma
|
||||
{
|
||||
protected readonly GUIComponent newGameContainer, loadGameContainer;
|
||||
|
||||
protected GUIListBox subList;
|
||||
protected GUIListBox saveList;
|
||||
protected List<GUITickBox> subTickBoxes;
|
||||
|
||||
protected GUITextBox saveNameBox, seedBox;
|
||||
|
||||
protected GUILayoutGroup subPreviewContainer;
|
||||
|
||||
protected GUIButton loadGameButton;
|
||||
|
||||
public Action<SubmarineInfo, string, string, CampaignSettings> StartNewGame;
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Globalization;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -14,55 +15,79 @@ namespace Barotrauma
|
||||
{
|
||||
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)
|
||||
{
|
||||
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,
|
||||
RelativeSpacing = 0.0f
|
||||
};
|
||||
|
||||
var leftColumn = new GUILayoutGroup(new RectTransform(Vector2.One, columnContainer.RectTransform))
|
||||
{
|
||||
Stretch = true,
|
||||
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)
|
||||
// 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);
|
||||
saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), verticalLayout.RectTransform) { MinSize = new Point(0, 20) }, string.Empty)
|
||||
{
|
||||
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);
|
||||
seedBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, ToolBox.RandomSeed(8));
|
||||
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.03f), verticalLayout.RectTransform) { MinSize = new Point(0, 20) }, ToolBox.RandomSeed(8));
|
||||
|
||||
// Spacing to fix the multiplayer campaign setup layout
|
||||
CreateMultiplayerCampaignSubList(leftColumn.RectTransform);
|
||||
|
||||
//spacing
|
||||
//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))
|
||||
GUIFrame radiationBoxContainer
|
||||
= new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), verticalLayout.RectTransform), style: null);
|
||||
GUITickBox radiationEnabledTickBox = null;
|
||||
if (MapGenerationParams.Instance.RadiationParams != null)
|
||||
{
|
||||
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 buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.12f),
|
||||
leftColumn.RectTransform) { MaxSize = new Point(int.MaxValue, 60) }, childAnchor: Anchor.BottomRight, isHorizontal: 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");
|
||||
|
||||
StartButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1f), buttonContainer.RectTransform, Anchor.BottomRight) { MaxSize = new Point(350, 60) }, TextManager.Get("StartCampaignButton"))
|
||||
void updateMissionCountText()
|
||||
{
|
||||
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);
|
||||
|
||||
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) =>
|
||||
{
|
||||
@@ -85,19 +110,18 @@ namespace Barotrauma
|
||||
|
||||
if (string.IsNullOrEmpty(selectedSub.MD5Hash.Hash))
|
||||
{
|
||||
((GUITextBlock)subList.SelectedComponent).TextColor = Color.DarkRed * 0.8f;
|
||||
subList.SelectedComponent.CanBeFocused = false;
|
||||
subList.Deselect();
|
||||
new GUIMessageBox(TextManager.Get("error"), TextManager.Get("nohashsubmarineselected"));
|
||||
return false;
|
||||
}
|
||||
|
||||
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveNameBox.Text);
|
||||
bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled;
|
||||
|
||||
CampaignSettings settings = new CampaignSettings();
|
||||
|
||||
settings.RadiationEnabled = GameMain.NetLobbyScreen.IsRadiationEnabled();
|
||||
settings.MaxMissionCount = GameMain.NetLobbyScreen.GetMaxMissionCount();
|
||||
CampaignSettings settings = new CampaignSettings
|
||||
{
|
||||
RadiationEnabled = radiationEnabledTickBox?.Selected ?? GameMain.NetworkMember.ServerSettings.RadiationEnabled,
|
||||
MaxMissionCount = maxMissionCount
|
||||
};
|
||||
|
||||
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages)
|
||||
{
|
||||
@@ -148,6 +172,8 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
@@ -163,114 +189,11 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
|
||||
columnContainer.Recalculate();
|
||||
leftColumn.Recalculate();
|
||||
rightColumn.Recalculate();
|
||||
verticalLayout.Recalculate();
|
||||
|
||||
if (submarines != null) { UpdateSubList(submarines); }
|
||||
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()
|
||||
{
|
||||
GUI.SetCursorWaiting();
|
||||
@@ -298,64 +221,6 @@ namespace Barotrauma
|
||||
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;
|
||||
public void UpdateLoadMenu(IEnumerable<string> saveFiles = null)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,10 @@ namespace Barotrauma
|
||||
{
|
||||
class SinglePlayerCampaignSetupUI : CampaignSetupUI
|
||||
{
|
||||
private GUIListBox subList;
|
||||
|
||||
protected GUILayoutGroup subPreviewContainer;
|
||||
|
||||
public CharacterInfo.AppearanceCustomizationMenu[] CharacterMenus { get; private set; }
|
||||
|
||||
private GUIButton nextButton;
|
||||
@@ -77,6 +81,7 @@ namespace Barotrauma
|
||||
{
|
||||
ScrollBarEnabled = false,
|
||||
ScrollBarVisible = false,
|
||||
AllowArrowKeyScroll = false,
|
||||
HoverCursor = CursorState.Default
|
||||
};
|
||||
|
||||
|
||||
@@ -705,7 +705,7 @@ namespace Barotrauma.CharacterEditor
|
||||
{
|
||||
string errorMsg = "Attempted to modify the state of the physics simulation while a time step was running.";
|
||||
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
|
||||
|
||||
@@ -732,9 +732,7 @@ namespace Barotrauma
|
||||
if (!string.IsNullOrEmpty(subName))
|
||||
{
|
||||
DebugConsole.NewMessage($"Loading the predefined quick start sub \"{subName}\"", Color.White);
|
||||
selectedSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s =>
|
||||
s.Name.ToLower() == subName.ToLower());
|
||||
|
||||
selectedSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name.ToLowerInvariant() == subName.ToLowerInvariant());
|
||||
if (selectedSub == null)
|
||||
{
|
||||
DebugConsole.NewMessage($"Cannot find a sub that matches the name \"{subName}\".", Color.Red);
|
||||
@@ -1055,17 +1053,23 @@ namespace Barotrauma
|
||||
|
||||
GUI.Draw(Cam, spriteBatch);
|
||||
|
||||
|
||||
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
|
||||
if (selectedTab != Tab.Credits)
|
||||
{
|
||||
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);
|
||||
for (int i = legalCrap.Length - 1; i >= 0; i--)
|
||||
{
|
||||
Vector2 textSize = GUI.SmallFont.MeasureString(legalCrap[i]);
|
||||
textSize = new Vector2((int)textSize.X, (int)textSize.Y);
|
||||
textSize = GUI.SmallFont.MeasureString(legalCrap[i])
|
||||
.ToPoint().ToVector2();
|
||||
bool mouseOn = i == 0 &&
|
||||
PlayerInput.MousePosition.X > textPos.X - textSize.X && PlayerInput.MousePosition.X < textPos.X &&
|
||||
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);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"MainMenuScreen.StartGame:IOException" + selectedSub.Name,
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"Copying the file \"" + selectedSub.FilePath + "\" failed.\n" + e.Message + "\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Copying a submarine file failed. " + e.Message + "\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1446,7 +1450,7 @@ namespace Barotrauma
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Fetching remote content to the main menu failed.", e);
|
||||
#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);
|
||||
return;
|
||||
}
|
||||
@@ -1489,7 +1493,7 @@ namespace Barotrauma
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Reading received remote main menu content failed.", e);
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Steam;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -40,12 +41,6 @@ namespace Barotrauma
|
||||
|
||||
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 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 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();
|
||||
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);
|
||||
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 =
|
||||
!CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
|
||||
@@ -2163,17 +2112,126 @@ namespace Barotrauma
|
||||
if (child != null) { PlayerList.RemoveChild(child); }
|
||||
}
|
||||
|
||||
public void SelectPlayer(GUITextBlock component, GUITextBlock.ClickableArea area)
|
||||
private Client ExtractClientFromClickableArea(GUITextBlock.ClickableArea area)
|
||||
{
|
||||
if (!UInt64.TryParse(area.Data.Metadata, out UInt64 id)) { return; }
|
||||
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);
|
||||
if (client == null) { return; }
|
||||
return client;
|
||||
}
|
||||
|
||||
public void SelectPlayer(GUITextBlock component, GUITextBlock.ClickableArea area)
|
||||
{
|
||||
var client = ExtractClientFromClickableArea(area);
|
||||
if (client is null) { return; }
|
||||
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)
|
||||
{
|
||||
bool myClient = selectedClient.ID == GameMain.Client.ID;
|
||||
@@ -2763,7 +2821,8 @@ namespace Barotrauma
|
||||
msg.ClickableAreas.Add(new GUITextBlock.ClickableArea()
|
||||
{
|
||||
Data = data,
|
||||
OnClick = GameMain.NetLobbyScreen.SelectPlayer
|
||||
OnClick = GameMain.NetLobbyScreen.SelectPlayer,
|
||||
OnSecondaryClick = GameMain.NetLobbyScreen.ShowPlayerContextMenu
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3221,6 +3280,7 @@ namespace Barotrauma
|
||||
{
|
||||
CreateSubPreview(submarine);
|
||||
}
|
||||
UpdateSubVisibility();
|
||||
}
|
||||
|
||||
private bool ViewJobInfo(GUIButton button, object obj)
|
||||
@@ -3369,14 +3429,28 @@ namespace Barotrauma
|
||||
return btn;
|
||||
}
|
||||
|
||||
public Pair<string, string> FailedSelectedSub;
|
||||
public Pair<string, string> FailedSelectedShuttle;
|
||||
public readonly struct FailedSubInfo
|
||||
{
|
||||
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 List<Pair<string, string>> FailedOwnedSubs = new List<Pair<string, string>>();
|
||||
public FailedSubInfo? FailedSelectedSub;
|
||||
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)
|
||||
{
|
||||
UpdateSubVisibility();
|
||||
if (GameMain.Client == null) { return false; }
|
||||
|
||||
//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 (subList == SubList)
|
||||
FailedSelectedSub = new Pair<string, string>(subName, md5Hash);
|
||||
{
|
||||
FailedSelectedSub = new FailedSubInfo(subName, md5Hash);
|
||||
}
|
||||
else
|
||||
FailedSelectedShuttle = new Pair<string, string>(subName, md5Hash);
|
||||
{
|
||||
FailedSelectedShuttle = new FailedSubInfo(subName, md5Hash);
|
||||
}
|
||||
|
||||
string errorMsg = "";
|
||||
if (sub == null || !SubmarineInfo.SavedSubmarines.Contains(sub))
|
||||
@@ -3542,22 +3620,22 @@ namespace Barotrauma
|
||||
{
|
||||
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 += (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")
|
||||
{
|
||||
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;
|
||||
};
|
||||
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),
|
||||
text: sub.Name)
|
||||
text: sub.DisplayName)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
@@ -3823,7 +3901,9 @@ namespace Barotrauma
|
||||
foreach (GUIComponent child in SubList.Content.Children)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2376,10 +2376,9 @@ namespace Barotrauma
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string errorMsg = "Failed to ping a server (" + ip + ") - " + (ex?.InnerException?.Message ?? ex.Message);
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + ip, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + ip, GameAnalyticsManager.ErrorSeverity.Warning, "Failed to ping a server - " + (ex?.InnerException?.Message ?? ex.Message));
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage(errorMsg, Color.Red);
|
||||
DebugConsole.NewMessage("Failed to ping a server (" + ip + ") - " + (ex?.InnerException?.Message ?? ex.Message), Color.Red);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -824,9 +824,8 @@ namespace Barotrauma
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = "Failed to save workshop item preview image to \"" + 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;
|
||||
}
|
||||
}
|
||||
@@ -1195,7 +1194,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Failed to edit workshop item (content package null)\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("SteamWorkshopScreen.ShowCreateItemFrame:ContentPackageNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("SteamWorkshopScreen.ShowCreateItemFrame:ContentPackageNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
displayName = TextManager.Get(fallbackTag, true);
|
||||
displayName = TextManager.Get(fallbackTag, true) ?? TextManager.Get($"sp.{fallbackTag}.name", true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace Barotrauma.Sounds
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
|
||||
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);
|
||||
int alError = Al.GetError();
|
||||
|
||||
@@ -470,14 +470,14 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Failed to update water ambience volume - submarine's movement value invalid (" + movementSoundVolume + ", sub velocity: " + sub.Velocity + ")";
|
||||
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;
|
||||
}
|
||||
if (!MathUtils.IsValid(insideSubFactor))
|
||||
{
|
||||
string errorMsg = "Failed to update water ambience volume - inside sub value invalid (" + insideSubFactor + ")";
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -609,7 +609,7 @@ namespace Barotrauma
|
||||
flowSoundChannels[i] = FlowSounds[i].Play(1.0f, FlowSoundRange, soundPos);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -790,7 +790,7 @@ namespace Barotrauma
|
||||
if (sound == null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace Barotrauma
|
||||
{
|
||||
float angle = 0.0f;
|
||||
float particleRotation = 0.0f;
|
||||
bool mirrorAngle = false;
|
||||
if (emitter.Prefab.Properties.CopyEntityAngle)
|
||||
{
|
||||
Limb targetLimb = null;
|
||||
@@ -71,7 +72,11 @@ namespace Barotrauma
|
||||
{
|
||||
angle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
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)
|
||||
{
|
||||
@@ -85,11 +90,15 @@ namespace Barotrauma
|
||||
{
|
||||
angle = targetLimb.body.Rotation + ((targetLimb.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (selectedSound.Sound.Disposed)
|
||||
|
||||
@@ -421,7 +421,7 @@ namespace Barotrauma
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Empty color array passed to the GradientLerp method.\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
#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());
|
||||
return Color.Black;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.15.15.0</Version>
|
||||
<Version>0.15.17.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
@@ -55,6 +55,7 @@
|
||||
<ItemGroup>
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.props" />
|
||||
<Compile Include="..\BarotraumaShared\**\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -99,7 +100,6 @@
|
||||
<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\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\Lidgren.Network\Lidgren.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\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\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\Lidgren.Network\Lidgren.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)" />
|
||||
</Target>
|
||||
|
||||
<PropertyGroup>
|
||||
<GARuntime>linux-x64</GARuntime>
|
||||
</PropertyGroup>
|
||||
<Import Project="../BarotraumaShared/DeployGameAnalytics.props" />
|
||||
|
||||
</Project>
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.15.15.0</Version>
|
||||
<Version>0.15.17.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
@@ -58,6 +58,7 @@
|
||||
<ItemGroup>
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.props" />
|
||||
<Compile Include="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\libsteam_api64.dylib" />
|
||||
<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\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\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\Lidgren.Network\Lidgren.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\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\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\Lidgren.Network\Lidgren.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)" />
|
||||
</Target>
|
||||
|
||||
<PropertyGroup>
|
||||
<GARuntime>osx-x64</GARuntime>
|
||||
</PropertyGroup>
|
||||
<Import Project="../BarotraumaShared/DeployGameAnalytics.props" />
|
||||
|
||||
</Project>
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.15.15.0</Version>
|
||||
<Version>0.15.17.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
@@ -63,6 +63,7 @@
|
||||
<ItemGroup>
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.props" />
|
||||
<Compile Include="..\BarotraumaShared\**\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -103,7 +104,6 @@
|
||||
<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\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\Lidgren.Network\Lidgren.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\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\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\Lidgren.Network\Lidgren.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)" />
|
||||
</Target>
|
||||
|
||||
<PropertyGroup>
|
||||
<GARuntime>win-x64</GARuntime>
|
||||
</PropertyGroup>
|
||||
<Import Project="../BarotraumaShared/DeployGameAnalytics.props" />
|
||||
|
||||
</Project>
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.15.15.0</Version>
|
||||
<Version>0.15.17.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
@@ -55,6 +55,7 @@
|
||||
<ItemGroup>
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.props" />
|
||||
<Compile Include="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Include="DedicatedServer.exe" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
@@ -62,18 +63,20 @@
|
||||
<ItemGroup Condition="'$(Configuration)'!='Debug'">
|
||||
<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\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\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(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\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\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RestSharp" Version="106.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Sourced from https://stackoverflow.com/a/45248069 -->
|
||||
<Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''">
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.15.15.0</Version>
|
||||
<Version>0.15.17.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
@@ -60,6 +60,7 @@
|
||||
<ItemGroup>
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.props" />
|
||||
<Compile Include="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\libsteam_api64.dylib" />
|
||||
<Content Remove="..\BarotraumaShared\libsteam_api64.so" />
|
||||
@@ -69,18 +70,20 @@
|
||||
<ItemGroup Condition="'$(Configuration)'!='Debug'">
|
||||
<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\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\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(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\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\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RestSharp" Version="106.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Sourced from https://stackoverflow.com/a/45248069 -->
|
||||
<ItemGroup>
|
||||
<None Include="..\BarotraumaShared\libsteam_api64.dylib">
|
||||
|
||||
@@ -269,7 +269,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Failed to write input to command line (window width: " + Console.WindowWidth + ", window height: " + Console.WindowHeight + ")\n"
|
||||
+ 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)
|
||||
{
|
||||
tpCharacter.TeleportTo(cursorWorldPos);
|
||||
if (tpCharacter.AIController?.SteeringManager is IndoorsSteeringManager pathSteering)
|
||||
{
|
||||
pathSteering.ResetPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Steam;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using GameAnalyticsSDK.Net;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -98,8 +97,9 @@ namespace Barotrauma
|
||||
|
||||
Console.WriteLine("Initializing SteamManager");
|
||||
SteamManager.Initialize();
|
||||
Console.WriteLine("Initializing GameAnalytics");
|
||||
if (GameSettings.SendUserStatistics) GameAnalyticsManager.Init();
|
||||
//TODO: figure out how consent is supposed to work for servers
|
||||
//Console.WriteLine("Initializing GameAnalytics");
|
||||
//GameAnalyticsManager.InitIfConsented();
|
||||
|
||||
Console.WriteLine("Initializing GameScreen");
|
||||
GameScreen = new GameScreen();
|
||||
@@ -418,7 +418,7 @@ namespace Barotrauma
|
||||
SaveUtil.CleanUnnecessarySaveFiles();
|
||||
|
||||
if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); }
|
||||
if (GameSettings.SendUserStatistics) { GameAnalytics.OnQuit(); }
|
||||
if (GameAnalyticsManager.SendUserStatistics) { GameAnalyticsManager.ShutDown(); }
|
||||
|
||||
MainThread = null;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Barotrauma
|
||||
get { return itemData != null; }
|
||||
}
|
||||
|
||||
public CharacterCampaignData(Client client, bool giveRespawnPenaltyAffliction = false)
|
||||
public CharacterCampaignData(Client client)
|
||||
{
|
||||
Name = client.Name;
|
||||
ClientEndPoint = client.Connection.EndPointString;
|
||||
@@ -22,13 +22,6 @@ namespace Barotrauma
|
||||
|
||||
healthData = new XElement("health");
|
||||
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)
|
||||
{
|
||||
itemData = new XElement("inventory");
|
||||
|
||||
@@ -211,18 +211,6 @@ namespace Barotrauma
|
||||
{
|
||||
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
|
||||
// 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;
|
||||
@@ -231,6 +219,7 @@ namespace Barotrauma
|
||||
if (characterInfo.CauseOfDeath != null && characterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected)
|
||||
{
|
||||
RespawnManager.ReduceCharacterSkills(characterInfo);
|
||||
characterInfo.RemoveSavedStatValuesOnDeath();
|
||||
}
|
||||
c.CharacterInfo = characterInfo;
|
||||
characterData.RemoveAll(cd => cd.MatchesClient(c));
|
||||
@@ -358,6 +347,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
UpdateCampaignSubs();
|
||||
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
PendingSubmarineSwitch = null;
|
||||
@@ -399,8 +389,6 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
CargoManager.OnItemsInBuyCrateChanged += () => { LastUpdateID++; };
|
||||
CargoManager.OnPurchasedItemsChanged += () => { LastUpdateID++; };
|
||||
@@ -409,11 +397,43 @@ namespace Barotrauma
|
||||
Map.OnLocationSelected += (loc, connection) => { 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
|
||||
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)
|
||||
{
|
||||
characterData.RemoveAll(cd => cd.MatchesClient(client));
|
||||
@@ -1030,14 +1050,6 @@ namespace Barotrauma
|
||||
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);
|
||||
|
||||
//save character data to a separate file
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (c.Character == null) { return; }
|
||||
var requestedFixAction = (FixActions)msg.ReadRangedInteger(0, 2);
|
||||
var QTESuccess = msg.ReadBoolean();
|
||||
if (requestedFixAction != FixActions.None)
|
||||
{
|
||||
if (!c.Character.IsTraitor && requestedFixAction == FixActions.Sabotage)
|
||||
@@ -31,6 +32,11 @@ namespace Barotrauma.Items.Components
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RepairBoost(QTESuccess);
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
@@ -42,6 +48,7 @@ namespace Barotrauma.Items.Components
|
||||
msg.Write(tinkeringStrength);
|
||||
msg.Write(CurrentFixer == null ? (ushort)0 : CurrentFixer.ID);
|
||||
msg.WriteRangedInteger((int)currentFixerAction, 0, 2);
|
||||
msg.Write(repairBoost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Barotrauma
|
||||
}
|
||||
msg.WriteRangedInteger((int)NetEntityEvent.Type.Invalid, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace Barotrauma
|
||||
msg.LengthBits = initialWritePos;
|
||||
msg.WriteRangedInteger((int)NetEntityEvent.Type.Invalid, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -275,6 +275,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
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);
|
||||
|
||||
List<string> lines = new List<string>();
|
||||
@@ -344,7 +346,7 @@ namespace Barotrauma.Networking
|
||||
catch (Exception e)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ namespace Barotrauma.Networking
|
||||
DebugConsole.ThrowError("FileSender threw an exception when trying to send data", e);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"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());
|
||||
transfer.Status = FileTransferStatus.Error;
|
||||
return;
|
||||
|
||||
@@ -600,10 +600,12 @@ namespace Barotrauma.Networking
|
||||
//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 (c.Connection != OwnerConnection) c.KickAFKTimer += deltaTime;
|
||||
if (c.Connection != OwnerConnection && c.Permissions != ClientPermissions.All) { c.KickAFKTimer += deltaTime; }
|
||||
}
|
||||
}
|
||||
|
||||
if (connectedClients.Any(c => c.KickAFKTimer >= serverSettings.KickAFKTime))
|
||||
{
|
||||
IEnumerable<Client> kickAFK = connectedClients.FindAll(c =>
|
||||
c.KickAFKTimer >= serverSettings.KickAFKTime &&
|
||||
(OwnerConnection == null || c.Connection != OwnerConnection));
|
||||
@@ -611,6 +613,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
KickClient(c, "DisconnectMessage.AFK");
|
||||
}
|
||||
}
|
||||
|
||||
serverPeer.Update(deltaTime);
|
||||
|
||||
@@ -632,7 +635,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
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();
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
@@ -641,7 +644,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"GameServer.Update:ClientWriteFailed" + e.StackTrace.CleanupStackTrace(),
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
errorMsg);
|
||||
}
|
||||
}
|
||||
@@ -791,6 +794,8 @@ namespace Barotrauma.Networking
|
||||
string localSavePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName);
|
||||
if (connectedClient.HasPermission(ClientPermissions.SelectMode) || connectedClient.HasPermission(ClientPermissions.ManageCampaign))
|
||||
{
|
||||
ServerSettings.RadiationEnabled = settings.RadiationEnabled;
|
||||
ServerSettings.MaxMissionCount = settings.MaxMissionCount;
|
||||
MultiPlayerCampaign.StartNewCampaign(localSavePath, matchingSub.FilePath, seed, settings);
|
||||
}
|
||||
}
|
||||
@@ -854,6 +859,7 @@ namespace Barotrauma.Networking
|
||||
private void HandleClientError(IReadMessage inc, Client c)
|
||||
{
|
||||
string errorStr = "Unhandled error report";
|
||||
string errorStrNoName = errorStr;
|
||||
|
||||
ClientNetError error = (ClientNetError)inc.ReadByte();
|
||||
switch (error)
|
||||
@@ -861,7 +867,7 @@ namespace Barotrauma.Networking
|
||||
case ClientNetError.MISSING_EVENT:
|
||||
UInt16 expectedID = 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;
|
||||
case ClientNetError.MISSING_ENTITY:
|
||||
UInt16 eventID = inc.ReadUInt16();
|
||||
@@ -869,25 +875,26 @@ namespace Barotrauma.Networking
|
||||
Entity entity = Entity.FindEntityByID(entityID);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
Log(GameServer.ClientLogName(c) + " has reported an error: " + errorStr, ServerLog.MessageType.Error);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:" + errorStr, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorStr);
|
||||
Log(ClientLogName(c) + " has reported an error: " + errorStr, ServerLog.MessageType.Error);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:" + errorStrNoName, GameAnalyticsManager.ErrorSeverity.Error, errorStr);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1392,16 +1399,13 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
break;
|
||||
case ClientPermissions.SelectSub:
|
||||
bool isCampaign = inc.ReadBoolean();
|
||||
if (!isCampaign)
|
||||
{
|
||||
bool isShuttle = inc.ReadBoolean();
|
||||
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);
|
||||
DebugConsole.NewMessage($"Client \"{ClientLogName(sender)}\" attempted to select a sub, index out of bounds ({subIndex})", Color.Red);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1414,29 +1418,6 @@ namespace Barotrauma.Networking
|
||||
GameMain.NetLobbyScreen.SelectedSub = subList[subIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int subEqualityCheckVal = inc.ReadInt32();
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (add)
|
||||
{
|
||||
GameMain.NetLobbyScreen.AddCampaignSubmarine(sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.NetLobbyScreen.RemoveCampaignSubmarine(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ClientPermissions.SelectMode:
|
||||
UInt16 modeIndex = inc.ReadUInt16();
|
||||
@@ -1751,7 +1732,7 @@ namespace Barotrauma.Networking
|
||||
" Chat message size: " + chatMessageBytes + " bytes\n" +
|
||||
" Position update size: " + positionUpdateBytes + " bytes\n\n";
|
||||
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);
|
||||
@@ -1791,7 +1772,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1878,7 +1859,7 @@ namespace Barotrauma.Networking
|
||||
List<int> campaignSubIndices = new List<int>();
|
||||
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++)
|
||||
{
|
||||
if (GameMain.NetLobbyScreen.CampaignSubmarines.Contains(subList[i]))
|
||||
@@ -1916,9 +1897,6 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
outmsg.Write(autoRestartTimerRunning ? serverSettings.AutoRestartTimer : 0.0f);
|
||||
}
|
||||
|
||||
outmsg.Write(serverSettings.RadiationEnabled);
|
||||
outmsg.Write((byte)serverSettings.MaxMissionCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1959,7 +1937,30 @@ namespace Barotrauma.Networking
|
||||
|
||||
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
|
||||
//of submarine files, so the message may have to be fragmented
|
||||
@@ -1975,28 +1976,6 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -2121,7 +2100,7 @@ namespace Barotrauma.Networking
|
||||
startGameCoroutine = null;
|
||||
string errorMsg = "Starting the round failed. Campaign was still active, but the map has been disposed. Try selecting another game mode.";
|
||||
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)
|
||||
{
|
||||
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).";
|
||||
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)
|
||||
{
|
||||
SendDirectChatMessage(errorMsg, connectedClients.Find(c => c.Connection == OwnerConnection), ChatMessageType.Error);
|
||||
@@ -2392,6 +2371,9 @@ namespace Barotrauma.Networking
|
||||
spawnedCharacter.TeamID = teamID;
|
||||
spawnedCharacter.GiveJobItems(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
|
||||
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();
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameServer.SendDirectChatMessage:ClientNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameServer.SendDirectChatMessage:ClientNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3255,7 +3237,7 @@ namespace Barotrauma.Networking
|
||||
BanClient(c, "ServerMessage.KickedByVoteAutoBan", duration: TimeSpan.FromSeconds(serverSettings.AutoBanTime));
|
||||
}
|
||||
|
||||
GameMain.NetLobbyScreen.LastUpdateID++;
|
||||
//GameMain.NetLobbyScreen.LastUpdateID++;
|
||||
|
||||
SendVoteStatus(connectedClients);
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace Barotrauma.Networking
|
||||
DebugConsole.ThrowError(errorMsg, e);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Barotrauma.Networking
|
||||
catch (Exception e)
|
||||
{
|
||||
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
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#else
|
||||
@@ -208,15 +208,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == inc.SenderConnection);
|
||||
|
||||
byte incByte = inc.ReadByte();
|
||||
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
|
||||
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
|
||||
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
|
||||
|
||||
if (isConnectionInitializationStep && pendingClient != null)
|
||||
if (packetHeader.IsConnectionInitializationStep() && pendingClient != null)
|
||||
{
|
||||
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;
|
||||
if (conn == null)
|
||||
@@ -242,7 +240,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Barotrauma.Networking
|
||||
catch (Exception e)
|
||||
{
|
||||
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
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#else
|
||||
@@ -125,14 +125,9 @@ namespace Barotrauma.Networking
|
||||
UInt64 senderSteamId = inc.ReadUInt64();
|
||||
UInt64 ownerSteamId = inc.ReadUInt64();
|
||||
|
||||
byte incByte = 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;
|
||||
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
|
||||
|
||||
if (isServerMessage)
|
||||
if (packetHeader.IsServerMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Got server message from" + senderSteamId.ToString());
|
||||
return;
|
||||
@@ -160,7 +155,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (isDisconnectMessage)
|
||||
else if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
if (pendingClient != null)
|
||||
{
|
||||
@@ -174,12 +169,12 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (isHeartbeatMessage)
|
||||
else if (packetHeader.IsHeartbeatMessage())
|
||||
{
|
||||
//message exists solely as a heartbeat, ignore its contents
|
||||
return;
|
||||
}
|
||||
else if (isConnectionInitializationStep)
|
||||
else if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
|
||||
if (pendingClient != null)
|
||||
@@ -203,7 +198,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -211,17 +206,17 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (OwnerConnection != null) { (OwnerConnection as SteamP2PConnection).Heartbeat(); }
|
||||
|
||||
if (isDisconnectMessage)
|
||||
if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received disconnect message from owner");
|
||||
return;
|
||||
}
|
||||
if (isServerMessage)
|
||||
if (packetHeader.IsServerMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received server message from owner");
|
||||
return;
|
||||
}
|
||||
if (isConnectionInitializationStep)
|
||||
if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
if (OwnerConnection == null)
|
||||
{
|
||||
@@ -236,7 +231,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isHeartbeatMessage)
|
||||
if (packetHeader.IsHeartbeatMessage())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -244,7 +239,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -267,7 +262,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
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);
|
||||
msgToSend.Write(conn.SteamID);
|
||||
msgToSend.Write((byte)deliveryMethod);
|
||||
|
||||
@@ -398,6 +398,7 @@ namespace Barotrauma.Networking
|
||||
else
|
||||
{
|
||||
ReduceCharacterSkills(characterInfos[i]);
|
||||
characterInfos[i].RemoveSavedStatValuesOnDeath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,17 @@ namespace Barotrauma.Networking
|
||||
public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.xml";
|
||||
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()
|
||||
{
|
||||
LoadSettings();
|
||||
@@ -37,6 +48,9 @@ namespace Barotrauma.Networking
|
||||
//outMsg.WritePadBits();
|
||||
//outMsg.Write((UInt16)QueryPort);
|
||||
|
||||
NetFlags requiredFlags = GetRequiredFlags(c);
|
||||
if (!requiredFlags.HasFlag(NetFlags.Properties)) { return; }
|
||||
|
||||
WriteNetProperties(outMsg);
|
||||
WriteMonsterEnabled(outMsg);
|
||||
BanList.ServerAdminWrite(outMsg, c);
|
||||
@@ -44,9 +58,19 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage outMsg, Client c)
|
||||
{
|
||||
NetFlags requiredFlags = GetRequiredFlags(c);
|
||||
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(HasPassword);
|
||||
outMsg.Write(IsPublic);
|
||||
@@ -54,12 +78,13 @@ namespace Barotrauma.Networking
|
||||
outMsg.WritePadBits();
|
||||
outMsg.WriteRangedInteger(TickRate, 1, 60);
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.Properties))
|
||||
{
|
||||
WriteExtraCargo(outMsg);
|
||||
}
|
||||
|
||||
WriteHiddenSubs(outMsg);
|
||||
|
||||
Voting.ServerWrite(outMsg);
|
||||
|
||||
if (c.HasPermission(Networking.ClientPermissions.ManageSettings))
|
||||
{
|
||||
outMsg.Write(true);
|
||||
@@ -85,20 +110,20 @@ namespace Barotrauma.Networking
|
||||
if (flags.HasFlag(NetFlags.Name))
|
||||
{
|
||||
string serverName = incMsg.ReadString();
|
||||
if (ServerName != serverName) changed = true;
|
||||
if (ServerName != serverName) { changed = true; }
|
||||
ServerName = serverName;
|
||||
}
|
||||
|
||||
if (flags.HasFlag(NetFlags.Message))
|
||||
{
|
||||
string serverMessageText = incMsg.ReadString();
|
||||
if (ServerMessageText != serverMessageText) changed = true;
|
||||
if (ServerMessageText != serverMessageText) { changed = true; }
|
||||
ServerMessageText = serverMessageText;
|
||||
}
|
||||
|
||||
if (flags.HasFlag(NetFlags.Properties))
|
||||
{
|
||||
changed |= ReadExtraCargo(incMsg);
|
||||
bool propertiesChanged = ReadExtraCargo(incMsg);
|
||||
|
||||
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);
|
||||
}
|
||||
changed = true;
|
||||
propertiesChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -124,10 +149,13 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
bool changedMonsterSettings = incMsg.ReadBoolean(); incMsg.ReadPadBits();
|
||||
changed |= changedMonsterSettings;
|
||||
if (changedMonsterSettings) ReadMonsterEnabled(incMsg);
|
||||
changed |= BanList.ServerAdminRead(incMsg, c);
|
||||
changed |= Whitelist.ServerAdminRead(incMsg, c);
|
||||
propertiesChanged |= changedMonsterSettings;
|
||||
if (changedMonsterSettings) { ReadMonsterEnabled(incMsg); }
|
||||
propertiesChanged |= BanList.ServerAdminRead(incMsg, c);
|
||||
propertiesChanged |= Whitelist.ServerAdminRead(incMsg, c);
|
||||
|
||||
if (propertiesChanged) { UpdateFlag(NetFlags.Properties); }
|
||||
changed |= propertiesChanged;
|
||||
}
|
||||
|
||||
if (flags.HasFlag(NetFlags.HiddenSubs))
|
||||
@@ -175,12 +203,14 @@ namespace Barotrauma.Networking
|
||||
MaxMissionCount = MathHelper.Clamp(maxMissionCount, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit);
|
||||
|
||||
changed |= true;
|
||||
UpdateFlag(NetFlags.Misc);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(NetFlags.LevelSeed))
|
||||
{
|
||||
GameMain.NetLobbyScreen.LevelSeed = incMsg.ReadString();
|
||||
changed |= true;
|
||||
UpdateFlag(NetFlags.LevelSeed);
|
||||
}
|
||||
|
||||
if (changed)
|
||||
@@ -271,6 +301,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
HiddenSubs.UnionWith(doc.Root.GetAttributeStringArray("HiddenSubs", Array.Empty<string>()));
|
||||
|
||||
SelectedSubmarine = SelectNonHiddenSubmarine(SelectedSubmarine);
|
||||
|
||||
string[] defaultAllowedClientNameChars =
|
||||
new string[] {
|
||||
"32-33",
|
||||
@@ -345,7 +377,6 @@ namespace Barotrauma.Networking
|
||||
|
||||
GameMain.NetLobbyScreen.SetBotSpawnMode(BotSpawnMode);
|
||||
GameMain.NetLobbyScreen.SetBotCount(BotCount);
|
||||
GameMain.NetLobbyScreen.SetMaxMissionCount(MaxMissionCount);
|
||||
|
||||
List<string> monsterNames = CharacterPrefab.Prefabs.Select(p => p.Identifier).ToList();
|
||||
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())
|
||||
{
|
||||
GameMain.NetLobbyScreen.SelectedSub = candidates.GetRandom(Rand.RandSync.Unsynced);
|
||||
return GameMain.NetLobbyScreen.SelectedSub.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
HiddenSubs.Remove(current);
|
||||
return current;
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
public void LoadClientPermissions()
|
||||
|
||||
@@ -69,6 +69,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
GameServer.Log("Saving whitelist", ServerLog.MessageType.ServerMessage);
|
||||
|
||||
GameMain.Server?.ServerSettings?.UpdateFlag(ServerSettings.NetFlags.Properties);
|
||||
|
||||
List<string> lines = new List<string>();
|
||||
|
||||
if (Enabled)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#region Using Statements
|
||||
|
||||
using Barotrauma.Steam;
|
||||
using GameAnalyticsSDK.Net;
|
||||
using System;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
#if LINUX
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Barotrauma
|
||||
Game = new GameMain(args);
|
||||
|
||||
Game.Run();
|
||||
if (GameSettings.SendUserStatistics) { GameAnalytics.OnQuit(); }
|
||||
if (GameAnalyticsManager.SendUserStatistics) { GameAnalyticsManager.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:");
|
||||
DebugConsole.Clear();
|
||||
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.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.");
|
||||
}
|
||||
else
|
||||
|
||||
@@ -51,42 +51,6 @@ namespace Barotrauma
|
||||
|
||||
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; }
|
||||
|
||||
private int selectedModeIndex;
|
||||
@@ -212,10 +176,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private List<SubmarineInfo> subs;
|
||||
public List<SubmarineInfo> GetSubList()
|
||||
{
|
||||
return subs;
|
||||
}
|
||||
public IReadOnlyList<SubmarineInfo> GetSubList() => subs;
|
||||
|
||||
public string LevelSeed
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.15.15.0</Version>
|
||||
<Version>0.15.17.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
@@ -61,6 +61,7 @@
|
||||
<ItemGroup>
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.props" />
|
||||
<Compile Include="..\BarotraumaShared\**\*.cs" />
|
||||
<Compile Remove="..\BarotraumaShared\SharedSource\Networking\Primitives\Message\WrapperMsg.cs" />
|
||||
<Content Remove="DedicatedServer.exe" />
|
||||
@@ -69,7 +70,6 @@
|
||||
<ItemGroup Condition="'$(Configuration)'!='Debug'">
|
||||
<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\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\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
|
||||
</ItemGroup>
|
||||
@@ -77,11 +77,14 @@
|
||||
<ItemGroup Condition="'$(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\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\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RestSharp" Version="106.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Sourced from https://stackoverflow.com/a/45248069 -->
|
||||
<Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''">
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -135,6 +135,7 @@
|
||||
<Submarine file="Submarines/Kastrull.sub" />
|
||||
<Submarine file="Submarines/KastrullDrone.sub" />
|
||||
<Submarine file="Submarines/Orca.sub" />
|
||||
<Submarine file="Submarines/Orca2.sub" />
|
||||
<Submarine file="Submarines/Remora.sub" />
|
||||
<Submarine file="Submarines/RemoraDrone.sub" />
|
||||
<Submarine file="Submarines/Selkie.sub" />
|
||||
|
||||
18
Barotrauma/BarotraumaShared/DeployGameAnalytics.props
Normal file
18
Barotrauma/BarotraumaShared/DeployGameAnalytics.props
Normal 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>
|
||||
@@ -1,8 +1,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using NLog.Targets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
@@ -125,6 +124,8 @@ namespace Barotrauma
|
||||
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 SelectTarget(AITarget target) { }
|
||||
@@ -306,14 +307,15 @@ namespace Barotrauma
|
||||
|
||||
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 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;
|
||||
if (inventory == null) { return; }
|
||||
int removed = 0;
|
||||
if (predicate == null || inventory.AllItems.Any(predicate))
|
||||
{
|
||||
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 (character.Inventory.TryPutItem(containedItem, character, CharacterInventory.anySlot))
|
||||
{
|
||||
if (unequipMax.HasValue && ++removed >= unequipMax) { return; }
|
||||
continue;
|
||||
}
|
||||
}
|
||||
containedItem.Drop(character);
|
||||
if (unequipMax.HasValue && ++removed >= unequipMax) { return; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Invalid AITarget sector direction (" + value + ")\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
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;
|
||||
}
|
||||
sectorDir = value;
|
||||
@@ -125,7 +125,7 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
#endif
|
||||
GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved",
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
return Vector2.Zero;
|
||||
}
|
||||
@@ -144,7 +144,7 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
#endif
|
||||
GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved",
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
return Vector2.Zero;
|
||||
}
|
||||
|
||||
@@ -339,11 +339,15 @@ namespace Barotrauma
|
||||
{
|
||||
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)
|
||||
{
|
||||
targetingTag = "owner";
|
||||
}
|
||||
else if (AIParams.TryGetTarget(targetCharacter.SpeciesName, out CharacterParams.TargetParams tP))
|
||||
else if (AIParams.TryGetTarget(targetCharacter, out CharacterParams.TargetParams tP))
|
||||
{
|
||||
targetingTag = tP.Tag;
|
||||
}
|
||||
@@ -353,7 +357,7 @@ namespace Barotrauma
|
||||
{
|
||||
targetingTag = "husk";
|
||||
}
|
||||
else
|
||||
else if (!Character.IsFriendly(targetCharacter))
|
||||
{
|
||||
if (enemy.CombatStrength > CombatStrength)
|
||||
{
|
||||
@@ -386,6 +390,10 @@ namespace Barotrauma
|
||||
{
|
||||
targetingTag = "sonar";
|
||||
}
|
||||
if (targetItem.GetComponent<Door>() != null)
|
||||
{
|
||||
targetingTag = "door";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (aiTarget.Entity is Structure)
|
||||
@@ -511,8 +519,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (avoidTimer <= 0 || activeTriggers.Any() && returnTimer <= 0)
|
||||
{
|
||||
CharacterParams.TargetParams targetingParams = null;
|
||||
UpdateTargets(Character, out targetingParams);
|
||||
UpdateTargets(out CharacterParams.TargetParams targetingParams);
|
||||
updateTargetsTimer = updateTargetsInterval * Rand.Range(0.75f, 1.25f);
|
||||
if (SelectedAiTarget == null)
|
||||
{
|
||||
@@ -1973,7 +1980,7 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
@@ -2362,7 +2369,7 @@ namespace Barotrauma
|
||||
//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
|
||||
//sight/hearing range
|
||||
public AITarget UpdateTargets(Character character, out CharacterParams.TargetParams targetingParams)
|
||||
public AITarget UpdateTargets(out CharacterParams.TargetParams targetingParams)
|
||||
{
|
||||
AITarget newTarget = null;
|
||||
targetValue = 0;
|
||||
@@ -2386,53 +2393,20 @@ namespace Barotrauma
|
||||
}
|
||||
Character targetCharacter = aiTarget.Entity as Character;
|
||||
//ignore the aitarget if it is the Character itself
|
||||
if (targetCharacter == character) { continue; }
|
||||
if (targetCharacter == Character) { continue; }
|
||||
|
||||
float valueModifier = 1;
|
||||
string targetingTag = null;
|
||||
string targetingTag = GetTargetingTag(aiTarget);
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
// ignore if target is tagged to be explicitly ignored (Feign Death)
|
||||
if (targetCharacter.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { continue; }
|
||||
|
||||
if (targetCharacter.IsDead)
|
||||
{
|
||||
targetingTag = "dead";
|
||||
}
|
||||
else if (PetBehavior != null && aiTarget.Entity == PetBehavior.Owner)
|
||||
{
|
||||
targetingTag = "owner";
|
||||
}
|
||||
else if (AIParams.TryGetTarget(targetCharacter.SpeciesName, out CharacterParams.TargetParams tP))
|
||||
{
|
||||
targetingTag = tP.Tag;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Character.IsFriendly(targetCharacter))
|
||||
if (AIParams.Targets.None() && Character.IsFriendly(targetCharacter))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (targetCharacter.AIController is EnemyAIController enemy)
|
||||
{
|
||||
if (targetCharacter.IsHusk && AIParams.HasTag("husk"))
|
||||
{
|
||||
targetingTag = "husk";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enemy.CombatStrength > CombatStrength)
|
||||
{
|
||||
targetingTag = "stronger";
|
||||
}
|
||||
else if (enemy.CombatStrength < CombatStrength)
|
||||
{
|
||||
targetingTag = "weaker";
|
||||
}
|
||||
else
|
||||
{
|
||||
targetingTag = "equal";
|
||||
}
|
||||
if (targetingTag == "stronger" && (State == AIState.Avoid || State == AIState.Escape || State == AIState.Flee))
|
||||
{
|
||||
if (SelectedAiTarget == aiTarget)
|
||||
@@ -2452,8 +2426,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore all structures, items, and hulls inside these subs.
|
||||
@@ -2469,7 +2441,7 @@ namespace Barotrauma
|
||||
if (aiTarget.Entity is Hull hull)
|
||||
{
|
||||
// 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
|
||||
if (hull.Submarine == null) { continue; }
|
||||
if (hull.Submarine.Info.IsRuin) { continue; }
|
||||
@@ -2479,7 +2451,7 @@ namespace Barotrauma
|
||||
if (aiTarget.Entity is Item item)
|
||||
{
|
||||
door = item.GetComponent<Door>();
|
||||
bool targetingFromOutsideToInside = item.CurrentHull != null && character.CurrentHull == null;
|
||||
bool targetingFromOutsideToInside = item.CurrentHull != null && Character.CurrentHull == null;
|
||||
if (targetingFromOutsideToInside)
|
||||
{
|
||||
if (door != null && (!canAttackDoors && !AIParams.CanOpenDoors) || !canAttackWalls)
|
||||
@@ -2488,30 +2460,14 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
}
|
||||
foreach (var prio in AIParams.Targets)
|
||||
if (door == null && targetingFromOutsideToInside)
|
||||
{
|
||||
if (item.HasTag(prio.Tag))
|
||||
{
|
||||
targetingTag = prio.Tag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (door == null && targetingTag == null)
|
||||
{
|
||||
if (item.GetComponent<Sonar>() != null)
|
||||
{
|
||||
targetingTag = "sonar";
|
||||
}
|
||||
else if (targetingFromOutsideToInside)
|
||||
{
|
||||
targetingTag = "room";
|
||||
if (item.Submarine?.Info.IsRuin != null)
|
||||
if (item.Submarine?.Info is { IsRuin: true })
|
||||
{
|
||||
// Ignore ruin items when the creature is outside.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (targetingTag == "nasonov")
|
||||
{
|
||||
if ((item.Submarine == null || !item.Submarine.Info.IsPlayer) && item.ParentInventory == null)
|
||||
@@ -2521,14 +2477,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
else if (aiTarget.Entity is Structure s)
|
||||
{
|
||||
targetingTag = "wall";
|
||||
if (!s.HasBody)
|
||||
{
|
||||
// Ignore structures that doesn't have a body (not walls)
|
||||
@@ -2537,7 +2492,7 @@ namespace Barotrauma
|
||||
if (s.IsPlatform) { continue; }
|
||||
if (s.Submarine == null) { continue; }
|
||||
if (s.Submarine.Info.IsRuin) { continue; }
|
||||
bool isCharacterInside = character.CurrentHull != null;
|
||||
bool isCharacterInside = Character.CurrentHull != null;
|
||||
bool isInnerWall = s.prefab.Tags.Contains("inner");
|
||||
if (isInnerWall && !isCharacterInside)
|
||||
{
|
||||
@@ -2624,21 +2579,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
targetingTag = "room";
|
||||
}
|
||||
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; }
|
||||
bool isOutdoor = door.LinkedGap?.FlowTargetHull != null && !door.LinkedGap.IsRoomToRoom;
|
||||
// Ignore inner doors when outside
|
||||
if (character.CurrentHull == null && !isOutdoor) { continue; }
|
||||
if (Character.CurrentHull == null && !isOutdoor) { continue; }
|
||||
bool isOpen = door.CanBeTraversed;
|
||||
if (!isOpen)
|
||||
{
|
||||
@@ -2651,7 +2597,7 @@ namespace Barotrauma
|
||||
}
|
||||
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
|
||||
if (door.CanBeTraversed)
|
||||
@@ -2679,14 +2625,14 @@ namespace Barotrauma
|
||||
if (targetingTag == null) { continue; }
|
||||
var targetParams = GetTargetParams(targetingTag);
|
||||
if (targetParams == null) { continue; }
|
||||
if (targetParams.IgnoreInside && character.CurrentHull != null) { continue; }
|
||||
if (targetParams.IgnoreOutside && character.CurrentHull == null) { continue; }
|
||||
if (targetParams.IgnoreInside && Character.CurrentHull != null) { continue; }
|
||||
if (targetParams.IgnoreOutside && Character.CurrentHull == null) { continue; }
|
||||
if (targetParams.IgnoreIncapacitated && targetCharacter != null && targetCharacter.IsIncapacitated) { continue; }
|
||||
if (targetParams.IgnoreIfNotInSameSub)
|
||||
{
|
||||
if (aiTarget.Entity.Submarine != Character.Submarine) { continue; }
|
||||
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)
|
||||
{
|
||||
@@ -2706,7 +2652,7 @@ namespace Barotrauma
|
||||
{
|
||||
target = selectedTargetingParams == targetParams ? targetParams.ThresholdMax : targetParams.ThresholdMin;
|
||||
}
|
||||
if (character.HealthPercentage > target)
|
||||
if (Character.HealthPercentage > target)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -2721,7 +2667,7 @@ namespace Barotrauma
|
||||
// Halve the priority for each swarm mate targeting the same target -> reduces stacking
|
||||
foreach (Character otherCharacter in SwarmBehavior.Members)
|
||||
{
|
||||
if (otherCharacter == character) { continue; }
|
||||
if (otherCharacter == Character) { continue; }
|
||||
if (otherCharacter.AIController?.SelectedAiTarget != aiTarget) { continue; }
|
||||
valueModifier /= 2;
|
||||
}
|
||||
@@ -2731,15 +2677,15 @@ namespace Barotrauma
|
||||
// The same as above, but using all the friendly characters in the level.
|
||||
foreach (Character otherCharacter in Character.CharacterList)
|
||||
{
|
||||
if (otherCharacter == character) { continue; }
|
||||
if (otherCharacter == Character) { continue; }
|
||||
if (otherCharacter.AIController?.SelectedAiTarget != aiTarget) { continue; }
|
||||
if (!character.IsFriendly(otherCharacter)) { continue; }
|
||||
if (!Character.IsFriendly(otherCharacter)) { continue; }
|
||||
valueModifier /= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!aiTarget.IsWithinSector(WorldPosition)) { continue; }
|
||||
Vector2 toTarget = aiTarget.WorldPosition - character.WorldPosition;
|
||||
Vector2 toTarget = aiTarget.WorldPosition - Character.WorldPosition;
|
||||
float dist = toTarget.Length();
|
||||
float nonModifiedDist = dist;
|
||||
//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);
|
||||
// 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)
|
||||
if (owner == character) { continue; }
|
||||
if (owner == Character) { continue; }
|
||||
if (owner != null && (Character.IsFriendly(owner) || owner.AiTarget != null && ignoredTargets.Contains(owner.AiTarget)))
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -85,6 +85,7 @@ namespace Barotrauma
|
||||
/// List of previous attacks done to this character
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
@@ -187,6 +188,15 @@ namespace Barotrauma
|
||||
foreach (var previousAttackResult in previousAttackResults)
|
||||
{
|
||||
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();
|
||||
respondToAttackTimer = RespondToAttackInterval;
|
||||
@@ -236,29 +246,34 @@ namespace Barotrauma
|
||||
bool hasValidPath = HasValidPath();
|
||||
|
||||
if (Character.Submarine == null)
|
||||
{
|
||||
// When the character is outside, far enough from the target, and the direct route is blocked,
|
||||
// use the indoor steering with the main and side path waypoints to help avoid getting stuck in level walls
|
||||
if (SelectedAiTarget?.Entity != null && !IsCloseEnoughToTarget(2000, useTargetSub: false))
|
||||
{
|
||||
obstacleRaycastTimer -= deltaTime;
|
||||
if (obstacleRaycastTimer <= 0)
|
||||
{
|
||||
obstacleRaycastTimer = obstacleRaycastIntervalLong;
|
||||
Vector2 rayEnd = SelectedAiTarget.Entity.SimPosition;
|
||||
if (SelectedAiTarget.Entity.Submarine != null)
|
||||
if (SelectedAiTarget?.Entity == null || SelectedAiTarget.Entity is ISpatialEntity target && target.Submarine == null || !IsCloseEnoughToTarget(2000, useTargetSub: false))
|
||||
{
|
||||
rayEnd += SelectedAiTarget.Entity.Submarine.SimPosition;
|
||||
// If the target is behind a level wall, switch to the pathing to get around the obstacles.
|
||||
ISpatialEntity spatialTarget = SelectedAiTarget?.Entity;
|
||||
if (spatialTarget == null)
|
||||
{
|
||||
var gotoObjective = ObjectiveManager.GetActiveObjective<AIObjectiveGoTo>();
|
||||
spatialTarget = gotoObjective?.Target;
|
||||
}
|
||||
IEnumerable<FarseerPhysics.Dynamics.Body> ignoredBodies = null;
|
||||
if (SelectedAiTarget.Entity is ISpatialEntity spatialTarget)
|
||||
if (spatialTarget == null)
|
||||
{
|
||||
UseIndoorSteeringOutside = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
IEnumerable<FarseerPhysics.Dynamics.Body> ignoredBodies = null;
|
||||
Vector2 rayEnd = spatialTarget.SimPosition;
|
||||
Submarine targetSub = spatialTarget.Submarine;
|
||||
if (targetSub != null)
|
||||
{
|
||||
rayEnd += targetSub.SimPosition;
|
||||
ignoredBodies = targetSub.PhysicsBody.FarseerBody.ToEnumerable();
|
||||
}
|
||||
}
|
||||
var obstacle = Submarine.PickBody(SimPosition, rayEnd, ignoredBodies, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall);
|
||||
UseIndoorSteeringOutside = obstacle != null;
|
||||
}
|
||||
@@ -267,9 +282,6 @@ namespace Barotrauma
|
||||
{
|
||||
UseIndoorSteeringOutside = false;
|
||||
if (hasValidPath)
|
||||
{
|
||||
obstacleRaycastTimer -= deltaTime;
|
||||
if (obstacleRaycastTimer <= 0)
|
||||
{
|
||||
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).
|
||||
@@ -333,24 +345,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the character is inside a cave
|
||||
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 (UseIndoorSteeringOutside || Character.CurrentHull?.Submarine != null || hasValidPath || IsCloseEnoughToTarget(steeringBuffer))
|
||||
{
|
||||
if (steeringManager != insideSteering)
|
||||
{
|
||||
@@ -601,7 +598,7 @@ namespace Barotrauma
|
||||
takeMaskOff = false;
|
||||
break;
|
||||
}
|
||||
else if (gotoObjective.mimic)
|
||||
else if (gotoObjective.Mimic)
|
||||
{
|
||||
if (!removeSuit)
|
||||
{
|
||||
@@ -880,7 +877,8 @@ namespace Barotrauma
|
||||
if (target.CurrentHull != hull || !target.Enabled) { continue; }
|
||||
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");
|
||||
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)
|
||||
{
|
||||
// The attack incapacitated/killed the character: respond immediately to trigger nearby characters because the update loop no longer runs
|
||||
@@ -1077,10 +1088,15 @@ namespace Barotrauma
|
||||
|
||||
private void RespondToAttack(Character attacker, AttackResult attackResult)
|
||||
{
|
||||
float healAmount = 0.0f;
|
||||
if (attacker != null)
|
||||
{
|
||||
previousHealAmounts.TryGetValue(attacker, out healAmount);
|
||||
}
|
||||
// excluding poisons etc
|
||||
float realDamage = attackResult.Damage;
|
||||
float realDamage = attackResult.Damage - healAmount;
|
||||
// including poisons etc
|
||||
float totalDamage = realDamage;
|
||||
float totalDamage = realDamage - healAmount;
|
||||
if (attackResult.Afflictions != null)
|
||||
{
|
||||
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).
|
||||
return;
|
||||
}
|
||||
float cumulativeDamage = GetDamageDoneByAttacker(attacker);
|
||||
float cumulativeDamage = Character.GetDamageDoneByAttacker(attacker);
|
||||
bool isAccidental = attacker.IsBot && !IsMentallyUnstable && !attacker.AIController.IsMentallyUnstable && Character.CombatAction == null;
|
||||
if (isAccidental)
|
||||
{
|
||||
@@ -1197,7 +1213,7 @@ namespace Barotrauma
|
||||
if (Character.Submarine != null && Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
|
||||
{
|
||||
// Non-friendly
|
||||
InformOtherNPCs(GetDamageDoneByAttacker(attacker));
|
||||
InformOtherNPCs(Character.GetDamageDoneByAttacker(attacker));
|
||||
}
|
||||
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; }
|
||||
Character thief = character;
|
||||
bool someoneSpoke = false;
|
||||
if (item == null || thief == null || item.GetComponent<LevelResource>() != null) { return; }
|
||||
|
||||
bool someoneSpoke = 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"))
|
||||
{
|
||||
foreach (Character otherCharacter in Character.CharacterList)
|
||||
{
|
||||
if (otherCharacter == thief || otherCharacter.TeamID == thief.TeamID || otherCharacter.IsDead ||
|
||||
otherCharacter.Info?.Job == null ||
|
||||
!(otherCharacter.AIController is HumanAIController otherHumanAI) ||
|
||||
if (otherCharacter == thief || otherCharacter.TeamID == thief.TeamID || otherCharacter.IsIncapacitated || otherCharacter.Stun > 0.0f ||
|
||||
otherCharacter.Info?.Job == null || !(otherCharacter.AIController is HumanAIController otherHumanAI) ||
|
||||
!otherHumanAI.VisibleHulls.Contains(thief.CurrentHull))
|
||||
{
|
||||
continue;
|
||||
@@ -1624,13 +1638,13 @@ namespace Barotrauma
|
||||
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
|
||||
// -> 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("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)
|
||||
{
|
||||
@@ -1663,7 +1677,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (item.OwnInventory?.FindItem(it => it.SpawnedInCurrentOutpost && !item.AllowStealing, true) is { } foundItem)
|
||||
{
|
||||
ItemTaken(foundItem, character);
|
||||
ItemTaken(foundItem, thief);
|
||||
}
|
||||
|
||||
bool TriggerSecurity(HumanAIController humanAI)
|
||||
@@ -1791,17 +1805,6 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
if (knownHulls.ContainsKey(hull))
|
||||
|
||||
@@ -26,10 +26,9 @@ namespace Barotrauma
|
||||
|
||||
private float findPathTimer;
|
||||
|
||||
private float checkDoorsTimer;
|
||||
private float buttonPressCooldown;
|
||||
|
||||
const float ButtonPressInterval = 0.25f;
|
||||
|
||||
public SteeringPath CurrentPath
|
||||
{
|
||||
get { return currentPath; }
|
||||
@@ -97,9 +96,10 @@ namespace Barotrauma
|
||||
public override void Update(float speed)
|
||||
{
|
||||
base.Update(speed);
|
||||
|
||||
buttonPressCooldown -= 1.0f / 60.0f;
|
||||
findPathTimer -= 1.0f / 60.0f;
|
||||
float step = 1.0f / 60.0f;
|
||||
checkDoorsTimer -= step;
|
||||
buttonPressCooldown -= step;
|
||||
findPathTimer -= step;
|
||||
}
|
||||
|
||||
public void SetPath(SteeringPath path)
|
||||
@@ -204,24 +204,23 @@ namespace Barotrauma
|
||||
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);
|
||||
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).
|
||||
if (IsIdenticalPath())
|
||||
{
|
||||
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).
|
||||
float t = (float)currentPath.CurrentIndex / (currentPath.Nodes.Count - 1);
|
||||
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.
|
||||
// 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.
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -315,7 +314,8 @@ namespace Barotrauma
|
||||
return currentTarget - pos2;
|
||||
}
|
||||
bool doorsChecked = false;
|
||||
if (!character.LockHands && buttonPressCooldown <= 0.0f)
|
||||
checkDoorsTimer = Math.Min(checkDoorsTimer, GetDoorCheckTime());
|
||||
if (!character.LockHands && checkDoorsTimer <= 0.0f)
|
||||
{
|
||||
CheckDoorsInPath();
|
||||
doorsChecked = true;
|
||||
@@ -336,7 +336,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.CanInteractWith(ladders.Item))
|
||||
{
|
||||
ladders.Item.TryInteract(character, false, true);
|
||||
ladders.Item.TryInteract(character, forceSelectKey: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -347,7 +347,7 @@ namespace Barotrauma
|
||||
if (previousLadders != null && previousLadders != ladders && character.SelectedConstruction != previousLadders.Item &&
|
||||
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)
|
||||
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)
|
||||
@@ -487,7 +487,14 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -500,8 +507,22 @@ namespace Barotrauma
|
||||
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()
|
||||
{
|
||||
checkDoorsTimer = GetDoorCheckTime();
|
||||
if (!canOpenDoors) { return; }
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
@@ -518,8 +539,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
bool closeDoors = character.IsBot && character.IsInFriendlySub || character.Params.AI != null && character.Params.AI.KeepDoorsClosed;
|
||||
if (i == 0 || !closeDoors)
|
||||
if (i == 0)
|
||||
{
|
||||
currentWaypoint = currentPath.CurrentNode;
|
||||
nextWaypoint = currentPath.NextNode;
|
||||
@@ -540,7 +560,7 @@ namespace Barotrauma
|
||||
if (currentWaypoint.ConnectedDoor.LinkedGap != null)
|
||||
{
|
||||
// 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;
|
||||
door = currentWaypoint.ConnectedDoor;
|
||||
@@ -567,15 +587,35 @@ namespace Barotrauma
|
||||
|
||||
if (door == null) { return; }
|
||||
|
||||
//toggle the door if it's the previous node and open, or if it's current node and closed
|
||||
if (door.BotsShouldKeepOpen) { shouldBeOpen = true; }
|
||||
|
||||
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;
|
||||
float closestDist = 0;
|
||||
bool canAccess = CanAccessDoor(door, button =>
|
||||
{
|
||||
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.IsOpen)
|
||||
{
|
||||
if (door.LinkedGap.IsHorizontal)
|
||||
{
|
||||
int dir = Math.Sign((nextWaypoint ?? currentWaypoint).WorldPosition.X - door.Item.WorldPosition.X);
|
||||
@@ -586,8 +626,9 @@ namespace Barotrauma
|
||||
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);
|
||||
if (closestButton == null || distance < closestDist)
|
||||
if (closestButton == null || distance < closestDist && character.CanSeeTarget(button.Item))
|
||||
{
|
||||
closestButton = button;
|
||||
closestDist = distance;
|
||||
@@ -596,18 +637,39 @@ namespace Barotrauma
|
||||
});
|
||||
if (canAccess)
|
||||
{
|
||||
bool pressButton = buttonPressCooldown <= 0 || lastDoor.door != door || lastDoor.state != shouldBeOpen;
|
||||
if (door.HasIntegratedButtons)
|
||||
{
|
||||
door.Item.TryInteract(character, false, true);
|
||||
buttonPressCooldown = ButtonPressInterval;
|
||||
if (pressButton && character.CanSeeTarget(door.Item))
|
||||
{
|
||||
if (door.Item.TryInteract(character, forceSelectKey: true))
|
||||
{
|
||||
lastDoor = (door, shouldBeOpen);
|
||||
buttonPressCooldown = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonPressCooldown = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
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);
|
||||
buttonPressCooldown = ButtonPressInterval;
|
||||
if (pressButton)
|
||||
{
|
||||
if (closestButton.Item.TryInteract(character, forceSelectKey: true))
|
||||
{
|
||||
lastDoor = (door, shouldBeOpen);
|
||||
buttonPressCooldown = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonPressCooldown = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -627,6 +689,7 @@ namespace Barotrauma
|
||||
// The button is on the wrong side of the door or a wall
|
||||
currentPath.Unreachable = true;
|
||||
}
|
||||
lastDoor = (null, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,8 @@ namespace Barotrauma
|
||||
var cells = Level.Loaded.GetCells(character.WorldPosition, 1);
|
||||
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.GraphEdge edge in cell.Edges)
|
||||
|
||||
@@ -283,9 +283,9 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public float CalculatePriority()
|
||||
{
|
||||
ForceWalk = false;
|
||||
Priority = GetPriority();
|
||||
ForceHighestPriority = false;
|
||||
ForceWalk = false;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,15 +68,12 @@ namespace Barotrauma
|
||||
protected override void OnObjectiveCompleted(AIObjective objective, Item 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.Submarine == null) { return false; }
|
||||
if (item.Submarine.TeamID != character.TeamID) { return false; }
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
if (!character.Submarine.IsConnectedTo(item.Submarine)) { return false; }
|
||||
}
|
||||
if (character.Submarine != null && !character.Submarine.IsConnectedTo(item.Submarine)) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -129,29 +126,7 @@ namespace Barotrauma
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool canEquip = true;
|
||||
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;
|
||||
return CanEquip(character, item);
|
||||
}
|
||||
|
||||
public override void OnDeselected()
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace Barotrauma
|
||||
public CombatMode Mode { get; private set; }
|
||||
|
||||
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 float AimSpeed => HumanAIController.AimSpeed;
|
||||
@@ -158,7 +158,7 @@ namespace Barotrauma
|
||||
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);
|
||||
return Priority;
|
||||
}
|
||||
@@ -177,11 +177,12 @@ namespace Barotrauma
|
||||
ignoredWeapons.Clear();
|
||||
ignoreWeaponTimer = ignoredWeaponsClearTime;
|
||||
}
|
||||
if (findSafety != null)
|
||||
bool isCurrentObjective = objectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>();
|
||||
if (findSafety != null && isCurrentObjective)
|
||||
{
|
||||
findSafety.Priority = 0;
|
||||
}
|
||||
if (!AllowCoolDown && !character.IsOnPlayerTeam && !objectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>())
|
||||
if (!AllowCoolDown && !character.IsOnPlayerTeam && !isCurrentObjective)
|
||||
{
|
||||
distanceTimer -= deltaTime;
|
||||
if (distanceTimer < 0)
|
||||
@@ -204,13 +205,18 @@ namespace Barotrauma
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
if (IsEnemyDisabled)
|
||||
{
|
||||
IsCompleted = true;
|
||||
return;
|
||||
}
|
||||
if (AllowCoolDown)
|
||||
{
|
||||
coolDownTimer -= deltaTime;
|
||||
}
|
||||
if (seekAmmunitionObjective == null && seekWeaponObjective == null)
|
||||
{
|
||||
if (Mode != CombatMode.Retreat && TryArm() && !IsEnemyDisabled)
|
||||
if (Mode != CombatMode.Retreat && TryArm())
|
||||
{
|
||||
OperateWeapon(deltaTime);
|
||||
}
|
||||
@@ -283,10 +289,12 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
AskHelp();
|
||||
Retreat(deltaTime);
|
||||
}
|
||||
break;
|
||||
case CombatMode.Retreat:
|
||||
AskHelp();
|
||||
Retreat(deltaTime);
|
||||
break;
|
||||
default:
|
||||
@@ -363,6 +371,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (WeaponComponent == null)
|
||||
{
|
||||
SpeakNoWeapons();
|
||||
Mode = CombatMode.Retreat;
|
||||
}
|
||||
}
|
||||
@@ -409,6 +418,7 @@ namespace Barotrauma
|
||||
onCompleted: () => RemoveSubObjective(ref seekWeaponObjective),
|
||||
onAbandon: () =>
|
||||
{
|
||||
SpeakNoWeapons();
|
||||
RemoveSubObjective(ref seekWeaponObjective);
|
||||
Mode = CombatMode.Retreat;
|
||||
});
|
||||
@@ -680,6 +690,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
SpeakNoWeapons();
|
||||
Weapon = null;
|
||||
Mode = CombatMode.Retreat;
|
||||
return false;
|
||||
@@ -892,7 +903,7 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref seekAmmunitionObjective,
|
||||
constructor: () => new AIObjectiveContainItem(character, ammunitionIdentifiers, Weapon.GetComponent<ItemContainer>(), objectiveManager)
|
||||
{
|
||||
targetItemCount = Weapon.GetComponent<ItemContainer>().Capacity,
|
||||
ItemCount = Weapon.GetComponent<ItemContainer>().Capacity,
|
||||
checkInventory = false
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref seekAmmunitionObjective),
|
||||
@@ -1099,7 +1110,7 @@ namespace Barotrauma
|
||||
if (WeaponComponent is RangedWeapon rangedWeapon)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
@@ -1155,6 +1166,21 @@ namespace Barotrauma
|
||||
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()
|
||||
//{
|
||||
// float enemyStrength = 0;
|
||||
|
||||
@@ -11,12 +11,11 @@ namespace Barotrauma
|
||||
|
||||
public Func<Item, float> GetItemPriority;
|
||||
|
||||
public int targetItemCount = 1;
|
||||
public string[] ignoredContainerIdentifiers;
|
||||
public bool checkInventory = true;
|
||||
|
||||
//if the item can't be found, spawn it in the character's inventory (used by outpost NPCs)
|
||||
private bool spawnItemIfNotFound = false;
|
||||
//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 readonly bool spawnItemIfNotFound;
|
||||
|
||||
//can either be a tag or an identifier
|
||||
public readonly string[] itemIdentifiers;
|
||||
@@ -35,9 +34,24 @@ namespace Barotrauma
|
||||
public bool Equip { get; set; }
|
||||
public bool RemoveEmpty { get; set; } = true;
|
||||
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; }
|
||||
|
||||
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)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
@@ -83,7 +97,7 @@ namespace Barotrauma
|
||||
containedItemCount++;
|
||||
}
|
||||
}
|
||||
return containedItemCount >= targetItemCount;
|
||||
return containedItemCount >= ItemCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,9 +120,9 @@ namespace Barotrauma
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -127,7 +141,6 @@ namespace Barotrauma
|
||||
container.Inventory.TryPutItem(item, null);
|
||||
}
|
||||
}
|
||||
|
||||
IsCompleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,11 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
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)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
@@ -86,6 +91,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (itemToDecontain.Container != sourceContainer.Item)
|
||||
{
|
||||
itemToDecontain.Drop(character);
|
||||
IsCompleted = true;
|
||||
return;
|
||||
}
|
||||
@@ -98,7 +104,12 @@ namespace Barotrauma
|
||||
if (getItemObjective == null && !itemToDecontain.IsOwnedBy(character))
|
||||
{
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@@ -110,6 +121,9 @@ namespace Barotrauma
|
||||
MoveWholeStack = TakeWholeStack,
|
||||
Equip = Equip,
|
||||
RemoveEmpty = false,
|
||||
RemoveExistingWhenNecessary = RemoveExistingWhenNecessary,
|
||||
RemoveExistingPredicate = RemoveExistingPredicate,
|
||||
RemoveMax = RemoveExistingMax,
|
||||
GetItemPriority = GetItemPriority,
|
||||
ignoredContainerIdentifiers = sourceContainer != null ? new string[] { sourceContainer.Item.Prefab.Identifier } : null
|
||||
},
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Barotrauma
|
||||
|
||||
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);
|
||||
if (character.TeamID == CharacterTeamType.FriendlyNPC && target.TeamID == CharacterTeamType.Team1 && GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
@@ -41,7 +41,7 @@ namespace Barotrauma
|
||||
combatObjective.holdFireCondition = () =>
|
||||
{
|
||||
//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));
|
||||
};
|
||||
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 (!character.Submarine.IsConnectedTo(target.Submarine)) { return false; }
|
||||
if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { return false; }
|
||||
if (ShouldArrest(target, character) && target.HasEquippedItem("handlocker")) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ShouldArrest(Character target, Character character)
|
||||
{
|
||||
return target != null && target.IsEscorted && character.TeamID == CharacterTeamType.Team1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,9 +52,11 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
Priority = (objectiveManager.IsCurrentOrder<AIObjectiveGoTo>() ||
|
||||
objectiveManager.IsCurrentOrder<AIObjectiveReturn>() ||
|
||||
objectiveManager.Objectives.Any(o => o.Priority > 0 && o is AIObjectiveCombat))
|
||||
Priority = (
|
||||
objectiveManager.HasOrder<AIObjectiveGoTo>(o => o.Priority > 0) ||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Barotrauma
|
||||
public float TargetCondition { get; set; } = 1;
|
||||
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)
|
||||
private bool spawnItemIfNotFound = false;
|
||||
@@ -58,6 +58,17 @@ namespace Barotrauma
|
||||
public bool EvaluateCombatPriority { get; set; }
|
||||
public bool CheckPathForEachItem { 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; }
|
||||
|
||||
@@ -81,7 +92,7 @@ namespace Barotrauma
|
||||
Equip = equip;
|
||||
this.spawnItemIfNotFound = spawnItemIfNotFound;
|
||||
this.checkInventory = checkInventory;
|
||||
this.identifiersOrTags = ParseGearTags(identifiersOrTags).ToImmutableArray();
|
||||
IdentifiersOrTags = ParseGearTags(identifiersOrTags).ToImmutableArray();
|
||||
ignoredIdentifiersOrTags = ParseIgnoredTags(identifiersOrTags).ToArray();
|
||||
}
|
||||
|
||||
@@ -113,7 +124,7 @@ namespace Barotrauma
|
||||
|
||||
private bool CheckInventory()
|
||||
{
|
||||
if (identifiersOrTags == null) { return false; }
|
||||
if (IdentifiersOrTags == null) { return false; }
|
||||
var item = character.Inventory.FindItem(i => CheckItem(i), recursive: true);
|
||||
if (item != null)
|
||||
{
|
||||
@@ -123,6 +134,19 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
if (character.LockHands)
|
||||
@@ -135,7 +159,7 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (identifiersOrTags != null && !isDoneSeeking)
|
||||
if (IdentifiersOrTags != null && !isDoneSeeking)
|
||||
{
|
||||
if (checkInventory)
|
||||
{
|
||||
@@ -152,7 +176,7 @@ namespace Barotrauma
|
||||
if (dangerousPressure)
|
||||
{
|
||||
#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);
|
||||
#endif
|
||||
Abandon = true;
|
||||
@@ -245,9 +269,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (IdentifiersOrTags == null)
|
||||
{
|
||||
IsCompleted = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsCompleted = CountItems();
|
||||
if (!IsCompleted)
|
||||
{
|
||||
ResetInternal();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Equip)
|
||||
{
|
||||
@@ -297,7 +332,7 @@ namespace Barotrauma
|
||||
|
||||
private void FindTargetItem()
|
||||
{
|
||||
if (identifiersOrTags == null)
|
||||
if (IdentifiersOrTags == 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.
|
||||
// This is relatively expensive, so let's do this only when it significantly improves the behavior.
|
||||
// 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 hasCalledPathFinder = false;
|
||||
@@ -430,10 +465,10 @@ namespace Barotrauma
|
||||
{
|
||||
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
|
||||
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
|
||||
Abandon = true;
|
||||
}
|
||||
@@ -452,7 +487,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
#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
|
||||
Abandon = true;
|
||||
}
|
||||
@@ -468,6 +503,12 @@ namespace Barotrauma
|
||||
// Not yet ready
|
||||
return false;
|
||||
}
|
||||
if (IdentifiersOrTags != null && ItemCount > 1)
|
||||
{
|
||||
return CountItems();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Equip && EquipSlotType.HasValue)
|
||||
{
|
||||
return character.HasEquippedItem(targetItem, EquipSlotType.Value);
|
||||
@@ -477,6 +518,7 @@ namespace Barotrauma
|
||||
return character.HasItem(targetItem, Equip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckItem(Item item)
|
||||
{
|
||||
@@ -487,7 +529,7 @@ namespace Barotrauma
|
||||
if (item.Condition < TargetCondition) { return false; }
|
||||
if (ItemFilter != null && !ItemFilter(item)) { 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()
|
||||
@@ -528,7 +570,7 @@ namespace Barotrauma
|
||||
{
|
||||
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)
|
||||
{
|
||||
character.Speak(msg, identifier: "dialogcannotfinditem", minDurationBetweenSimilar: 20.0f);
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Barotrauma
|
||||
public override string Identifier { get; set; } = "get items";
|
||||
public override string DebugTag => $"{Identifier}";
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowMultipleInstances => true;
|
||||
|
||||
public bool AllowStealing { get; set; }
|
||||
public bool TakeWholeStack { get; set; }
|
||||
@@ -21,6 +22,7 @@ namespace Barotrauma
|
||||
public bool EvaluateCombatPriority { get; set; }
|
||||
public bool CheckPathForEachItem { get; set; }
|
||||
public bool RequireLoaded { get; set; }
|
||||
public bool RequireAllItems { get; set; }
|
||||
|
||||
private readonly ImmutableArray<string> gearTags;
|
||||
private readonly string[] ignoredTags;
|
||||
@@ -47,9 +49,11 @@ namespace Barotrauma
|
||||
{
|
||||
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;
|
||||
TryAddSubObjective(ref getItem, () =>
|
||||
new AIObjectiveGetItem(character, tag, objectiveManager, Equip, CheckInventory)
|
||||
new AIObjectiveGetItem(character, tag, objectiveManager, Equip, CheckInventory && count <= 1)
|
||||
{
|
||||
AllowVariants = AllowVariants,
|
||||
Wear = Wear,
|
||||
@@ -57,7 +61,9 @@ namespace Barotrauma
|
||||
AllowStealing = AllowStealing,
|
||||
ignoredIdentifiersOrTags = ignoredTags,
|
||||
CheckPathForEachItem = CheckPathForEachItem,
|
||||
RequireLoaded = RequireLoaded
|
||||
RequireLoaded = RequireLoaded,
|
||||
ItemCount = count,
|
||||
SpeakIfFails = RequireAllItems
|
||||
},
|
||||
onCompleted: () =>
|
||||
{
|
||||
@@ -75,6 +81,10 @@ namespace Barotrauma
|
||||
achievedItems.Remove(item);
|
||||
}
|
||||
RemoveSubObjective(ref getItem);
|
||||
if (RequireAllItems)
|
||||
{
|
||||
Abandon = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
subObjectivesCreated = true;
|
||||
|
||||
@@ -22,16 +22,23 @@ namespace Barotrauma
|
||||
public Func<bool> requiredCondition;
|
||||
public Func<PathNode, bool> endNodeFilter;
|
||||
|
||||
public Func<float> priorityGetter;
|
||||
public Func<float> PriorityGetter;
|
||||
|
||||
public bool IsFollowOrderObjective;
|
||||
public bool Mimic;
|
||||
|
||||
public bool isFollowOrderObjective;
|
||||
public bool mimic;
|
||||
public bool SpeakIfFails { get; set; } = true;
|
||||
public bool DebugLogWhenFails { get; set; } = true;
|
||||
public bool UsePathingOutside { get; set; } = true;
|
||||
|
||||
public float extraDistanceWhileSwimming;
|
||||
public float extraDistanceOutsideSub;
|
||||
public float ExtraDistanceWhileSwimming;
|
||||
public float ExtraDistanceOutsideSub;
|
||||
private float _closeEnoughMultiplier = 1;
|
||||
public float CloseEnoughMultiplier
|
||||
{
|
||||
get { return _closeEnoughMultiplier; }
|
||||
set { _closeEnoughMultiplier = Math.Max(value, 1); }
|
||||
}
|
||||
private float _closeEnough = 50;
|
||||
private readonly float minDistance = 50;
|
||||
private readonly float seekGapsInterval = 1;
|
||||
@@ -45,14 +52,15 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
float dist = _closeEnough;
|
||||
float dist = _closeEnough * CloseEnoughMultiplier;
|
||||
float extraMultiplier = Math.Clamp(CloseEnoughMultiplier * 0.6f, 1, 3);
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
dist += extraDistanceWhileSwimming;
|
||||
dist += ExtraDistanceWhileSwimming * extraMultiplier;
|
||||
}
|
||||
if (character.CurrentHull == null)
|
||||
{
|
||||
dist += extraDistanceOutsideSub;
|
||||
dist += ExtraDistanceOutsideSub * extraMultiplier;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
@@ -109,9 +117,9 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (priorityGetter != null)
|
||||
if (PriorityGetter != null)
|
||||
{
|
||||
Priority = priorityGetter();
|
||||
Priority = PriorityGetter();
|
||||
}
|
||||
else if (OverridePriority.HasValue)
|
||||
{
|
||||
@@ -177,7 +185,7 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (cannotFollow || Target == character || character.SelectedBy != null && HumanAIController.IsFriendly(character.SelectedBy))
|
||||
if (Target == character || character.SelectedBy != null && HumanAIController.IsFriendly(character.SelectedBy))
|
||||
{
|
||||
// Wait
|
||||
character.AIController.SteeringManager.Reset();
|
||||
@@ -200,7 +208,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
Hull targetHull = GetTargetHull();
|
||||
if (!isFollowOrderObjective)
|
||||
if (!IsFollowOrderObjective)
|
||||
{
|
||||
// Abandon if going through unsafe paths. Note ignores unsafe nodes when following an order or when the objective is set to ignore unsafe hulls.
|
||||
bool containsUnsafeNodes = character.IsDismissed && !HumanAIController.ObjectiveManager.CurrentObjective.IgnoreUnsafeHulls
|
||||
@@ -249,7 +257,7 @@ namespace Barotrauma
|
||||
Character followTarget = Target as Character;
|
||||
bool needsDivingSuit = (!isInside || hasOutdoorNodes) && character.NeedsAir && !character.HasAbilityFlag(AbilityFlags.ImmuneToPressure);
|
||||
bool needsDivingGear = (needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit)) && character.NeedsAir;
|
||||
if (mimic)
|
||||
if (Mimic)
|
||||
{
|
||||
if (HumanAIController.HasDivingSuit(followTarget) && character.NeedsAir)
|
||||
{
|
||||
@@ -277,7 +285,7 @@ namespace Barotrauma
|
||||
if (findDivingGear != null && !findDivingGear.CanBeCompleted)
|
||||
{
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: false, objectiveManager),
|
||||
onAbandon: () => Abort(),
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () =>
|
||||
{
|
||||
cannotFollow = false;
|
||||
@@ -287,7 +295,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager),
|
||||
onAbandon: () => Abort(),
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () =>
|
||||
{
|
||||
cannotFollow = false;
|
||||
@@ -320,7 +328,7 @@ namespace Barotrauma
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
if (character.CurrentHull == null ||
|
||||
isFollowOrderObjective &&
|
||||
IsFollowOrderObjective &&
|
||||
targetCharacter != null && (targetCharacter.CurrentHull == null) != (character.CurrentHull == null) &&
|
||||
Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition) < maxGapDistance * maxGapDistance)
|
||||
{
|
||||
@@ -363,7 +371,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (TargetGap != null)
|
||||
{
|
||||
if (TargetGap.FlowTargetHull != null && HumanAIController.SteerThroughGap(TargetGap, isFollowOrderObjective ? Target.WorldPosition : TargetGap.FlowTargetHull.WorldPosition, deltaTime))
|
||||
if (TargetGap.FlowTargetHull != null && HumanAIController.SteerThroughGap(TargetGap, IsFollowOrderObjective ? Target.WorldPosition : TargetGap.FlowTargetHull.WorldPosition, deltaTime))
|
||||
{
|
||||
SteeringManager.SteeringAvoid(deltaTime, avoidLookAheadDistance, weight: 1);
|
||||
return;
|
||||
@@ -382,7 +390,7 @@ namespace Barotrauma
|
||||
Item scooter = null;
|
||||
float closeEnough = 250;
|
||||
float squaredDistance = Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition);
|
||||
bool shouldUseScooter = squaredDistance > closeEnough * closeEnough && (!mimic ||
|
||||
bool shouldUseScooter = squaredDistance > closeEnough * closeEnough && (!Mimic ||
|
||||
(targetCharacter != null && targetCharacter.HasEquippedItem(scooterTag, allowBroken: false)) || squaredDistance > Math.Pow(closeEnough * 2, 2));
|
||||
if (HumanAIController.HasItem(character, scooterTag, out IEnumerable<Item> equippedScooters, recursive: false, requireEquipped: true))
|
||||
{
|
||||
@@ -390,6 +398,13 @@ namespace Barotrauma
|
||||
scooter = equippedScooters.FirstOrDefault();
|
||||
}
|
||||
else if (shouldUseScooter)
|
||||
{
|
||||
var leftHandItem = character.GetEquippedItem(slotType: InvSlotType.LeftHand);
|
||||
var rightHandItem = character.GetEquippedItem(slotType: InvSlotType.RightHand);
|
||||
bool handsFull =
|
||||
(leftHandItem != null && character.Inventory.CheckIfAnySlotAvailable(leftHandItem, inWrongSlot: false) == -1) ||
|
||||
(rightHandItem != null && character.Inventory.CheckIfAnySlotAvailable(rightHandItem, inWrongSlot: false) == -1);
|
||||
if (!handsFull)
|
||||
{
|
||||
bool hasBattery = false;
|
||||
if (HumanAIController.HasItem(character, scooterTag, out IEnumerable<Item> nonEquippedScooters, containedTag: batteryTag, conditionPercentage: 1, requireEquipped: false))
|
||||
@@ -411,6 +426,7 @@ namespace Barotrauma
|
||||
HumanAIController.TakeItem(scooter, character.Inventory, equip: true, dropOtherIfCannotMove: false, allowSwapping: true, storeUnequipped: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isScooterEquipped = scooter != null && character.HasEquippedItem(scooter);
|
||||
if (scooter != null && isScooterEquipped)
|
||||
{
|
||||
@@ -597,7 +613,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (gap.Open < 1) { continue; }
|
||||
if (gap.Submarine == null) { continue; }
|
||||
if (!isFollowOrderObjective)
|
||||
if (!IsFollowOrderObjective)
|
||||
{
|
||||
if (gap.FlowTargetHull == null) { continue; }
|
||||
if (gap.Submarine != Target.Submarine) { continue; }
|
||||
@@ -678,18 +694,6 @@ namespace Barotrauma
|
||||
return IsCompleted;
|
||||
}
|
||||
|
||||
private void Abort()
|
||||
{
|
||||
if (!objectiveManager.IsOrder(this))
|
||||
{
|
||||
Abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cannotFollow = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAbandon()
|
||||
{
|
||||
StopMovement();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user