Unstable v0.10.600.0

This commit is contained in:
Juan Pablo Arce
2020-10-01 12:19:24 -03:00
parent 20a69375ca
commit ebe1ce1427
217 changed files with 4284 additions and 1547 deletions
@@ -414,7 +414,8 @@ namespace Barotrauma
if (playSound)
{
var damageSound = character.GetSound(s => s.Type == CharacterSound.SoundType.Damage);
SoundPlayer.PlayDamageSound(limbJoint.Params.BreakSound, 1.0f, limbJoint.LimbA.body.DrawPosition, range: damageSound != null ? damageSound.Range : 800);
float range = damageSound != null ? damageSound.Range * 2 : ConvertUnits.ToDisplayUnits(character.AnimController.Collider.GetSize().Length() * 10);
SoundPlayer.PlayDamageSound(limbJoint.Params.BreakSound, 1.0f, limbJoint.LimbA.body.DrawPosition, range: range);
}
}
@@ -426,10 +427,10 @@ namespace Barotrauma
if (Limbs == null)
{
DebugConsole.ThrowError("Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace);
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);
"Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
@@ -559,6 +559,18 @@ namespace Barotrauma
partial void UpdateProjSpecific(float deltaTime, Camera cam)
{
if (InvisibleTimer > 0.0f)
{
if (Controlled == null || (Controlled.CharacterHealth.GetAffliction("psychosis")?.Strength ?? 0.0f) <= 0.0f)
{
InvisibleTimer = 0.0f;
}
else
{
InvisibleTimer -= deltaTime;
}
}
if (!enabled) { return; }
if (!IsDead && !IsIncapacitated)
@@ -653,7 +665,7 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch, Camera cam)
{
if (!Enabled) { return; }
if (!Enabled || InvisibleTimer > 0.0f) { return; }
AnimController.Draw(spriteBatch, cam);
}
@@ -665,7 +677,7 @@ namespace Barotrauma
public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam)
{
if (!Enabled) { return; }
if (!Enabled || InvisibleTimer > 0.0f) { return; }
if (GameMain.DebugDraw)
{
@@ -11,7 +11,7 @@ namespace Barotrauma
{
class CharacterHUD
{
private static Dictionary<Entity, int> orderIndicatorCount = new Dictionary<Entity, int>();
private static Dictionary<ISpatialEntity, int> orderIndicatorCount = new Dictionary<ISpatialEntity, int>();
const float ItemOverlayDelay = 1.0f;
private static Item focusedItem;
private static float focusedItemOverlayTimer;
@@ -458,6 +458,8 @@ namespace Barotrauma
private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order, float iconAlpha = 1.0f)
{
if (order?.SymbolSprite == null) { return; }
if (order.TargetAllCharacters)
{
if (order.OrderGiver != character && !order.HasAppropriateJob(character))
@@ -466,7 +468,7 @@ namespace Barotrauma
}
}
Entity target = order.ConnectedController != null ? order.ConnectedController.Item : order.TargetEntity;
ISpatialEntity target = order.ConnectedController != null ? order.ConnectedController.Item : order.TargetSpatialEntity;
if (target == null) { return; }
//don't show the indicator if far away and not inside the same sub
@@ -479,7 +481,9 @@ namespace Barotrauma
if (!orderIndicatorCount.ContainsKey(target)) { orderIndicatorCount.Add(target, 0); }
Vector2 drawPos = target.DrawPosition + Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target];
Vector2 drawPos = target is Entity ? (target as Entity).DrawPosition :
target.Submarine == null ? target.Position : target.Position + target.Submarine.DrawPosition;
drawPos += Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target];
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha);
orderIndicatorCount[target] = orderIndicatorCount[target] + 1;
@@ -154,7 +154,7 @@ namespace Barotrauma
GUI.Style.Green,
textPopupPos,
Vector2.UnitY * 10.0f,
playSound: Character.Controlled?.Info == this);
playSound: false);
}
else if (prevLevel % 0.1f > 0.05f && newLevel % 0.1f < 0.05f)
{
@@ -163,7 +163,7 @@ namespace Barotrauma
GUI.Style.Green,
textPopupPos,
Vector2.UnitY * 10.0f,
playSound: Character.Controlled?.Info == this);
playSound: false);
}
if ((int)newLevel > (int)prevLevel)
@@ -1,4 +1,5 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
@@ -444,14 +445,25 @@ namespace Barotrauma
Entity targetEntity = FindEntityByID(inc.ReadUInt16());
Character orderGiver = inc.ReadBoolean() ? FindEntityByID(inc.ReadUInt16()) as Character : null;
int orderOptionIndex = inc.ReadByte();
OrderTarget targetPosition = null;
if (inc.ReadBoolean())
{
var x = inc.ReadSingle();
var y = inc.ReadSingle();
var hull = FindEntityByID(inc.ReadUInt16()) as Hull;
targetPosition = new OrderTarget(new Vector2(x, y), hull, true);
}
if (orderPrefabIndex >= 0 && orderPrefabIndex < Order.PrefabList.Count)
{
var orderPrefab = Order.PrefabList[orderPrefabIndex];
if (!orderPrefab.MustSetTarget || (targetEntity != null && (targetEntity as Item).Components.Any(c => c?.GetType() == orderPrefab.ItemComponentType)))
var component = orderPrefab.GetTargetItemComponent(targetEntity as Item);
if (!orderPrefab.MustSetTarget || (targetEntity != null && component != null) || targetPosition != null)
{
character.SetOrder(
new Order(orderPrefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(c => c?.GetType() == orderPrefab.ItemComponentType), orderGiver: orderGiver),
var order = targetPosition == null ?
new Order(orderPrefab, targetEntity, component, orderGiver: orderGiver) :
new Order(orderPrefab, targetPosition, orderGiver: orderGiver);
character.SetOrder(order,
orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderOptionIndex] : null,
orderGiver, speak: false);
}
@@ -1,38 +1,26 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Barotrauma
{
partial class AfflictionPsychosis : Affliction
{
class FakeFireSource
{
public Vector2 Size;
public Vector2 Position;
public Hull Hull;
public float LifeTime;
}
const int MaxFakeFireSources = 10;
const float MinFakeFireSourceInterval = 30.0f, MaxFakeFireSourceInterval = 240.0f;
private float createFireSourceTimer;
private readonly List<FakeFireSource> fakeFireSources = new List<FakeFireSource>();
private readonly List<DummyFireSource> fakeFireSources = new List<DummyFireSource>();
enum FloodType
public enum FloodType
{
None,
Minor,
Major,
HideFlooding
}
const float MinSoundInterval = 10.0f, MaxSoundInterval = 180.0f;
const float MinSoundInterval = 60.0f, MaxSoundInterval = 240.0f;
private FloodType currentFloodType;
private float soundTimer;
@@ -44,12 +32,21 @@ namespace Barotrauma
private float fakeBrokenInterval = 30.0f;
private float fakeBrokenTimer = 0.0f;
private float invisibleCharacterInterval = 30.0f;
private float invisibleCharacterTimer = 0.0f;
public FloodType CurrentFloodType
{
get { return currentFloodType; }
}
partial void UpdateProjSpecific(CharacterHealth characterHealth, Limb targetLimb, float deltaTime)
{
if (Character.Controlled != characterHealth.Character) return;
UpdateFloods(deltaTime);
UpdateSounds(characterHealth.Character, deltaTime);
UpdateFires(characterHealth.Character, deltaTime);
UpdateInvisibleCharacters(deltaTime);
UpdateFakeBroken(deltaTime);
}
@@ -77,10 +74,14 @@ namespace Barotrauma
{
case FloodType.Minor:
currentFloodState += deltaTime;
//lerp the water surface in all hulls 50 units above the floor within 10 seconds
//lerp the water surface in all hulls 15 units above the floor within 10 seconds
foreach (Hull hull in Hull.hullList)
{
hull.DrawSurface = hull.Rect.Y - hull.Rect.Height + MathHelper.Lerp(0.0f, 50.0f, currentFloodState / 10.0f);
for (int i = hull.FakeFireSources.Count - 1; i >= 0; i--)
{
hull.FakeFireSources[i].Extinguish(deltaTime, 50.0f);
}
hull.DrawSurface = hull.Rect.Y - hull.Rect.Height + MathHelper.Lerp(0.0f, 15.0f, currentFloodState / 10.0f);
}
break;
case FloodType.Major:
@@ -88,6 +89,10 @@ namespace Barotrauma
//create a full flood in 10 seconds
foreach (Hull hull in Hull.hullList)
{
for (int i = hull.FakeFireSources.Count - 1; i >= 0; i--)
{
hull.FakeFireSources[i].Extinguish(deltaTime, 200.0f);
}
hull.DrawSurface = hull.Rect.Y - MathHelper.Lerp(hull.Rect.Height, 0.0f, currentFloodState / 10.0f);
}
break;
@@ -104,6 +109,7 @@ namespace Barotrauma
if (createFloodTimer < MathHelper.Lerp(MaxFloodInterval, MinFloodInterval, Strength / 100.0f))
{
currentFloodType = FloodType.None;
createFloodTimer += deltaTime;
return;
}
@@ -114,10 +120,12 @@ namespace Barotrauma
if (Rand.Range(0.0f, 1.0f) < 0.5f)
{
currentFloodType = FloodType.HideFlooding;
currentFloodType = FloodType.Minor;
}
else
{
currentFloodType = Strength < 50.0f ? FloodType.Minor : FloodType.Major;
//disabled Major flooding because it's too easy to tell it's fake
currentFloodType = FloodType.Minor;// Strength < 50.0f ? FloodType.Minor : FloodType.Major;
}
currentFloodDuration = Rand.Range(20.0f, 100.0f);
}
@@ -127,47 +135,46 @@ namespace Barotrauma
private void UpdateFires(Character character, float deltaTime)
{
createFireSourceTimer += deltaTime;
fakeFireSources.RemoveAll(fs => fs.Removed);
if (fakeFireSources.Count < MaxFakeFireSources &&
character.Submarine != null &&
createFireSourceTimer > MathHelper.Lerp(MaxFakeFireSourceInterval, MinFakeFireSourceInterval, Strength / 100.0f))
{
Hull fireHull = Hull.hullList.GetRandom(h => h.Submarine == character.Submarine);
fakeFireSources.Add(new FakeFireSource()
if (fireHull != null)
{
Size = Vector2.One * 20.0f,
Hull = fireHull,
Position = new Vector2(Rand.Range(0.0f, fireHull.Rect.Width), 30.0f),
LifeTime = MathHelper.Lerp(10.0f, 100.0f, Strength / 100.0f)
});
createFireSourceTimer = 0.0f;
}
foreach (FakeFireSource fakeFireSource in fakeFireSources)
{
if (fakeFireSource.Hull.DrawSurface > fakeFireSource.Hull.Rect.Y - fakeFireSource.Hull.Rect.Height + fakeFireSource.Position.Y)
{
fakeFireSource.LifeTime -= deltaTime * 100.0f;
var fakeFire = new DummyFireSource(Vector2.One * 500.0f, new Vector2(Rand.Range(fireHull.WorldRect.X, fireHull.WorldRect.Right), fireHull.WorldPosition.Y), fireHull, isNetworkMessage: true)
{
CausedByPsychosis = true,
DamagesItems = false,
DamagesCharacters = false
};
fakeFireSources.Add(fakeFire);
createFireSourceTimer = 0.0f;
}
}
}
fakeFireSource.LifeTime -= deltaTime;
float growAmount = deltaTime * 5.0f;
fakeFireSource.Size.X += growAmount;
fakeFireSource.Position.X = MathHelper.Clamp(fakeFireSource.Position.X - growAmount / 2.0f, 0.0f, fakeFireSource.Hull.Rect.Width);
fakeFireSource.Position.Y = MathHelper.Clamp(fakeFireSource.Position.Y, 0.0f, fakeFireSource.Hull.Rect.Height);
fakeFireSource.Size.X = Math.Min(fakeFireSource.Hull.Rect.Width - fakeFireSource.Position.X, fakeFireSource.Size.X);
fakeFireSource.Size.Y = Math.Min(fakeFireSource.Hull.Rect.Height - fakeFireSource.Position.Y, fakeFireSource.Size.Y);
private void UpdateInvisibleCharacters(float deltaTime)
{
invisibleCharacterTimer -= deltaTime;
if (invisibleCharacterTimer > 0.0f) { return; }
FireSource.EmitParticles(
fakeFireSource.Size,
fakeFireSource.Hull.WorldRect.Location.ToVector2() + fakeFireSource.Position - Vector2.UnitY * fakeFireSource.Hull.Rect.Height,
fakeFireSource.Hull,
0.5f);
foreach (Character c in Character.CharacterList)
{
if (c.IsDead || c == Character.Controlled) { continue; }
if (c.WorldPosition.X < GameMain.GameScreen.Cam.WorldView.X || c.WorldPosition.X > GameMain.GameScreen.Cam.WorldView.Right) { continue; }
if (c.WorldPosition.Y < GameMain.GameScreen.Cam.WorldView.Y - GameMain.GameScreen.Cam.WorldView.Height || c.WorldPosition.Y > GameMain.GameScreen.Cam.WorldView.Y) { continue; }
if (Rand.Range(0.0f, 500.0f) < Strength)
{
c.InvisibleTimer = 60.0f;
}
}
fakeFireSources.RemoveAll(fs => fs.LifeTime <= 0.0f);
invisibleCharacterTimer = invisibleCharacterInterval;
}
private void UpdateFakeBroken(float deltaTime)
{
fakeBrokenTimer -= deltaTime;
@@ -545,7 +545,7 @@ namespace Barotrauma
private void OnAttacked(Character attacker, AttackResult attackResult)
{
if (Math.Abs(attackResult.Damage) < 0.01f && attackResult.Afflictions.Count == 0) { return; }
if (Math.Abs(attackResult.Damage) < 0.01f) { return; }
DamageOverlayTimer = MathHelper.Clamp(attackResult.Damage / MaxVitality, DamageOverlayTimer, 1.0f);
if (healthShadowDelay <= 0.0f) { healthShadowDelay = 1.0f; }
@@ -611,17 +611,41 @@ namespace Barotrauma
}
}
partial void UpdateOxygenProjSpecific(float prevOxygen)
private float timeUntilNextHeartbeatSound = 0.0f;
private bool nextHeartbeatSoundIsSystole = true;
private const string diastoleSoundTag = "heartbeatdiastole", systoleSoundTag = "heartbeatsystole";
partial void UpdateOxygenProjSpecific(float prevOxygen, float deltaTime)
{
if (prevOxygen > 0.0f && OxygenAmount <= 0.0f &&
Character.Controlled == Character)
if (prevOxygen > 0.0f && OxygenAmount <= 0.0f && Character.Controlled == Character)
{
SoundPlayer.PlaySound(Character.Info != null && Character.Info.Gender == Gender.Female ? "drownfemale" : "drownmale");
}
if (Character == Character.Controlled && !IsUnconscious && !Character.IsDead && OxygenAmount < LowOxygenThreshold)
{
timeUntilNextHeartbeatSound -= deltaTime;
if (timeUntilNextHeartbeatSound < 0.0f)
{
if (nextHeartbeatSoundIsSystole)
{
SoundPlayer.PlaySound(systoleSoundTag, 1.0f - (OxygenAmount / LowOxygenThreshold));
timeUntilNextHeartbeatSound = MathHelper.Lerp(0.18f, 0.3f, Math.Clamp(OxygenAmount / InsufficientOxygenThreshold, 0.0f, 1.0f));
}
else
{
SoundPlayer.PlaySound(diastoleSoundTag, 1.0f - (OxygenAmount / LowOxygenThreshold));
timeUntilNextHeartbeatSound = MathHelper.Lerp(0.3f, 0.5f, Math.Clamp(OxygenAmount / InsufficientOxygenThreshold, 0.0f, 1.0f));
}
nextHeartbeatSoundIsSystole = !nextHeartbeatSoundIsSystole;
}
}
}
partial void UpdateBleedingProjSpecific(AfflictionBleeding affliction, Limb targetLimb, float deltaTime)
{
if (Character.InvisibleTimer > 0.0f) { return; }
bloodParticleTimer -= deltaTime * (affliction.Strength / 10.0f);
if (bloodParticleTimer <= 0.0f)
{
@@ -664,7 +688,7 @@ namespace Barotrauma
{
forceAfflictionContainerUpdate = true;
currentDisplayedAfflictions = GetAllAfflictions(mergeSameAfflictions: true)
.FindAll(a => a.Strength >= a.Prefab.ShowIconThreshold && a.Prefab.Icon != null);
.FindAll(a => a.ShouldShowIcon(Character) && a.Prefab.Icon != null);
currentDisplayedAfflictions.Sort((a1, a2) =>
{
int dmgPerSecond = Math.Sign(a2.DamagePerSecond - a1.DamagePerSecond);
@@ -1147,7 +1171,7 @@ namespace Barotrauma
afflictionIconContainer.Content.ClearChildren();
return;
}
var currentAfflictions = GetMatchingAfflictions(selectedLimb, a => a.Strength >= a.Prefab.ShowIconThreshold);
var currentAfflictions = GetMatchingAfflictions(selectedLimb, a => a.ShouldShowIcon(Character));
var displayedAfflictions = afflictionIconContainer.Content.Children.Select(c => c.UserData as Affliction);
if (currentAfflictions.Any(a => !displayedAfflictions.Contains(a)) ||
displayedAfflictions.Any(a => !currentAfflictions.Contains(a)))
@@ -1675,9 +1699,9 @@ namespace Barotrauma
var tempAfflictions = GetMatchingAfflictions(limbHealth, a => true);
float negativeEffect = tempAfflictions.Where(a => !a.Prefab.IsBuff && a.Strength >= a.Prefab.ShowIconThreshold).Sum(a => a.Strength);
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.Strength >= a.Prefab.ShowIconThreshold).Sum(a => a.Strength * 0.2f);
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 midPoint = (float)limbEffectiveArea.Center.Y / (float)limbHealth.IndicatorSprite.Texture.Height;
@@ -1786,11 +1810,11 @@ namespace Barotrauma
i = 0;
foreach (LimbHealth limbHealth in limbHealths)
{
IEnumerable<Affliction> thisAfflictions = limbHealth.Afflictions.Where(a => a.Strength >= a.Prefab.ShowIconThreshold);
IEnumerable<Affliction> thisAfflictions = limbHealth.Afflictions.Where(a => a.ShouldShowIcon(Character));
thisAfflictions = thisAfflictions.Concat(afflictions.Where(a =>
{
Limb indicatorLimb = Character.AnimController.GetLimb(a.Prefab.IndicatorLimb);
return (indicatorLimb != null && indicatorLimb.HealthIndex == i && a.Strength >= a.Prefab.ShowIconThreshold);
return indicatorLimb != null && indicatorLimb.HealthIndex == i && a.ShouldShowIcon(Character);
}));
if (thisAfflictions.Count() <= 0) { i++; continue; }
@@ -1837,12 +1861,14 @@ namespace Barotrauma
private void DrawLimbAfflictionIcon(SpriteBatch spriteBatch, Affliction affliction, float iconScale, ref Vector2 iconPos)
{
if (affliction.Strength < affliction.Prefab.ShowIconThreshold) return;
Vector2 iconSize = (affliction.Prefab.Icon.size * iconScale);
if (!affliction.ShouldShowIcon(Character)) { return; }
Vector2 iconSize = affliction.Prefab.Icon.size * iconScale;
float showIconThreshold = Character.Controlled?.CharacterHealth == this ? affliction.Prefab.ShowIconThreshold : affliction.Prefab.ShowIconToOthersThreshold;
//afflictions that have a strength of less than 10 are faded out slightly
float alpha = MathHelper.Lerp(0.3f, 1.0f,
(affliction.Strength - affliction.Prefab.ShowIconThreshold) / Math.Min(affliction.Prefab.MaxStrength - affliction.Prefab.ShowIconThreshold, 10.0f));
(affliction.Strength - showIconThreshold) / Math.Min(affliction.Prefab.MaxStrength - showIconThreshold, 10.0f));
affliction.Prefab.Icon.Draw(spriteBatch, iconPos - iconSize / 2.0f, GetAfflictionIconColor(affliction.Prefab, affliction) * alpha, 0, iconScale);
iconPos += new Vector2(10.0f, 20.0f) * iconScale;
@@ -8,5 +8,12 @@
get;
private set;
}
[Serialize("", false), Editable]
public string DamageParticle
{
get;
private set;
}
}
}
@@ -459,18 +459,16 @@ namespace Barotrauma
}
}
float damageMultiplier = 1;
float bleedingDamageMultiplier = 1;
foreach (DamageModifier damageModifier in result.AppliedDamageModifiers)
{
foreach (var afflictionPrefab in AfflictionPrefab.List)
if (damageModifier.MatchesAfflictionType("damage"))
{
if (damageModifier.MatchesAffliction(afflictionPrefab.Identifier, afflictionPrefab.AfflictionType))
{
if (afflictionPrefab.Effects.Any(e => e.MaxVitalityDecrease > 0))
{
damageMultiplier *= damageModifier.DamageMultiplier;
break;
}
}
damageMultiplier *= damageModifier.DamageMultiplier;
}
else if (damageModifier.MatchesAfflictionType("bleeding"))
{
bleedingDamageMultiplier *= damageModifier.DamageMultiplier;
}
}
if (playSound)
@@ -488,20 +486,29 @@ namespace Barotrauma
}
// spawn damage particles
float damageParticleAmount = Math.Min(damage / 5, 1.0f) * damageMultiplier;
float damageParticleAmount = damage < 1 ? 0 : Math.Min(damage / 5, 1.0f) * damageMultiplier;
if (damageParticleAmount > 0.001f)
{
foreach (ParticleEmitter emitter in character.DamageEmitters)
{
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
emitter.Emit(1.0f, WorldPosition, character.CurrentHull, amountMultiplier: damageParticleAmount);
ParticlePrefab overrideParticle = null;
foreach (DamageModifier damageModifier in result.AppliedDamageModifiers)
{
if (damageModifier.DamageMultiplier > 0 && !string.IsNullOrWhiteSpace(damageModifier.DamageParticle))
{
overrideParticle = GameMain.ParticleManager?.FindPrefab(damageModifier.DamageParticle);
break;
}
}
emitter.Emit(1.0f, WorldPosition, character.CurrentHull, amountMultiplier: damageParticleAmount, overrideParticle: overrideParticle);
}
}
if (bleedingDamage > 0)
{
float bloodParticleAmount = Math.Min(bleedingDamage / 5, 1.0f) * damageMultiplier;
float bloodParticleAmount = Math.Min(bleedingDamage / 5, 1.0f) * bleedingDamageMultiplier;
float bloodParticleSize = MathHelper.Clamp(bleedingDamage / 5, 0.1f, 1.0f);
foreach (ParticleEmitter emitter in character.BloodEmitters)
@@ -930,6 +937,10 @@ namespace Barotrauma
{
depth -= depthStep;
}
if (wearableItemComponent.AllowedSlots.Contains(InvSlotType.Bag))
{
depth -= depthStep * 2;
}
wearableColor = wearableItemComponent.Item.GetSpriteColor();
}
float textureScale = wearable.InheritTextureScale ? TextureScale : 1;
@@ -2575,7 +2575,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);
GameAnalyticsManager.AddErrorEventOnce("DebugConsole.SpawnSubmarine:Error", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + '\n' + e.Message + '\n' + e.StackTrace.CleanupStackTrace());
}
},
() =>
@@ -109,7 +109,7 @@ namespace Barotrauma
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{
SetChannel(radio.Channel - 1, setText: true);
GUI.PlayUISound(GUISoundType.PopupMenu);
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
}
return true;
}
@@ -142,7 +142,7 @@ namespace Barotrauma
{
int.TryParse(channelText.Text, out int newChannel);
SetChannel(newChannel, setText: true);
GUI.PlayUISound(GUISoundType.PopupMenu);
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
};
var buttonRight = new GUIButton(new RectTransform(new Vector2(0.1f, 0.8f), channelSettingsContent.RectTransform), style: "DeviceButton")
@@ -152,7 +152,7 @@ namespace Barotrauma
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{
SetChannel(radio.Channel + 1, setText: true);
GUI.PlayUISound(GUISoundType.PopupMenu);
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
}
return true;
}
@@ -194,7 +194,7 @@ namespace Barotrauma
channelText.Flash(GUI.Style.Green);
}
SetChannel(radio.GetChannelMemory(index), setText: true);
GUI.PlayUISound(GUISoundType.PopupMenu);
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
}
return true;
}
@@ -469,7 +469,7 @@ namespace Barotrauma
soundType = GUISoundType.DeadMessage;
}
GUI.PlayUISound(soundType);
SoundPlayer.PlayUISound(soundType);
}
public void SetVisibility(bool visible)
@@ -205,6 +205,7 @@ namespace Barotrauma
};
validateHiresButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaigncrew.validate"))
{
ClickSound = GUISoundType.HireRepairClick,
ForceUpperCase = true,
OnClicked = (b, o) => ValidatePendingHires(true)
};
@@ -390,12 +391,29 @@ namespace Barotrauma
if (listBox == hireableList)
{
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
var hireButton = new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
{
UserData = characterInfo,
Enabled = HasPermission,
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
};
hireButton.OnAddedToGUIUpdateList += (GUIComponent btn) =>
{
if (PendingHires.Count + campaign.CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
{
if (btn.Enabled)
{
btn.ToolTip = TextManager.Get("canthiremorecharacters");
btn.Enabled = false;
}
}
else if (!btn.Enabled)
{
btn.ToolTip = string.Empty;
btn.Enabled = true;
}
};
}
else if (listBox == pendingList)
{
@@ -514,6 +532,11 @@ namespace Barotrauma
private bool AddPendingHire(CharacterInfo characterInfo, bool createNetworkMessage = true)
{
if (PendingHires.Count + campaign.CrewManager.GetCharacters().Count() >= CrewManager.MaxCrewSize)
{
return false;
}
hireableList.Content.RemoveChild(hireableList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo));
hireableList.UpdateScrollBarSize();
if (!PendingHires.Contains(characterInfo)) { PendingHires.Add(characterInfo); }
@@ -27,7 +27,11 @@ namespace Barotrauma
PickItem,
PickItemFail,
DropItem,
PopupMenu
PopupMenu,
DecreaseQuantity,
IncreaseQuantity,
HireRepairClick,
UISwitch
}
public enum CursorState
@@ -135,7 +139,7 @@ namespace Barotrauma
public static GraphicsDevice GraphicsDevice { get; private set; }
private static List<GUIMessage> messages = new List<GUIMessage>();
private static Sound[] sounds;
private static readonly Dictionary<GUISoundType, string> soundIdentifiers = new Dictionary<GUISoundType, string>();
private static bool pauseMenuOpen, settingsMenuOpen;
public static GUIFrame PauseMenu { get; private set; }
private static Sprite arrow;
@@ -256,23 +260,13 @@ namespace Barotrauma
}
}
public static void LoadContent(bool loadSounds = true)
public static void LoadContent()
{
if (loadSounds)
foreach (GUISoundType soundType in Enum.GetValues(typeof(GUISoundType)))
{
sounds = new Sound[Enum.GetValues(typeof(GUISoundType)).Length];
sounds[(int)GUISoundType.UIMessage] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/UImsg.ogg", false);
sounds[(int)GUISoundType.ChatMessage] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/ChatMsg.ogg", false);
sounds[(int)GUISoundType.RadioMessage] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/RadioMsg.ogg", false);
sounds[(int)GUISoundType.DeadMessage] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/DeadMsg.ogg", false);
sounds[(int)GUISoundType.Click] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/Click.ogg", false);
sounds[(int)GUISoundType.PopupMenu] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/PopupMenu.ogg", false);
sounds[(int)GUISoundType.PickItem] = GameMain.SoundManager.LoadSound("Content/Sounds/PickItem.ogg", false);
sounds[(int)GUISoundType.PickItemFail] = GameMain.SoundManager.LoadSound("Content/Sounds/PickItemFail.ogg", false);
sounds[(int)GUISoundType.DropItem] = GameMain.SoundManager.LoadSound("Content/Sounds/DropItem.ogg", false);
soundIdentifiers.Add(soundType, soundType.ToString().ToLowerInvariant());
}
// create 1x1 texture for line drawing
CrossThread.RequestExecutionOnMainThread(() =>
{
@@ -314,7 +308,7 @@ namespace Barotrauma
#if UNSTABLE
string line1 = "Barotrauma Unstable v" + GameMain.Version;
string line2 = "(" + AssemblyInfo.GetBuildString() + ", branch " + AssemblyInfo.GetGitBranch() + ", revision " + AssemblyInfo.GetGitRevision() + ")";
string line2 = "(" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")";
Rectangle watermarkRect = new Rectangle(-50, GameMain.GraphicsHeight - 80, 50 + (int)(Math.Max(LargeFont.MeasureString(line1).X, Font.MeasureString(line2).X) * 1.2f), 100);
float alpha = 1.0f;
@@ -2282,13 +2276,13 @@ namespace Barotrauma
{
if (messages.Any(msg => msg.Text == message)) { return; }
messages.Add(new GUIMessage(message, color, lifeTime ?? MathHelper.Clamp(message.Length / 5.0f, 3.0f, 10.0f), font ?? LargeFont));
if (playSound) PlayUISound(GUISoundType.UIMessage);
if (playSound) SoundPlayer.PlayUISound(GUISoundType.UIMessage);
}
public static void AddMessage(string message, Color color, Vector2 worldPos, Vector2 velocity, float lifeTime = 3.0f, bool playSound = true)
public static void AddMessage(string message, Color color, Vector2 worldPos, Vector2 velocity, float lifeTime = 3.0f, bool playSound = true, GUISoundType soundType = GUISoundType.UIMessage)
{
messages.Add(new GUIMessage(message, color, worldPos, velocity, lifeTime, Alignment.Center, LargeFont));
if (playSound) PlayUISound(GUISoundType.UIMessage);
if (playSound) SoundPlayer.PlayUISound(soundType);
}
public static void ClearMessages()
@@ -2296,16 +2290,6 @@ namespace Barotrauma
messages.Clear();
}
public static void PlayUISound(GUISoundType soundType)
{
if (sounds == null) { return; }
int soundIndex = (int)soundType;
if (soundIndex < 0 || soundIndex >= sounds.Length) { return; }
sounds[soundIndex]?.Play(null, "ui");
}
public static bool IsFourByThree()
{
float aspectRatio = HorizontalAspectRatio;
@@ -157,6 +157,8 @@ namespace Barotrauma
private float pulseTimer;
private float pulseExpand;
private bool flashed;
public GUISoundType ClickSound { get; set; } = GUISoundType.Click;
public GUIButton(RectTransform rectT, string text = "", Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : base(style, rectT)
{
@@ -247,7 +249,7 @@ namespace Barotrauma
}
else if (PlayerInput.PrimaryMouseButtonClicked())
{
GUI.PlayUISound(GUISoundType.Click);
SoundPlayer.PlayUISound(ClickSound);
if (OnClicked != null)
{
if (OnClicked(this, UserData))
@@ -404,7 +404,7 @@ namespace Barotrauma
/// <param name="component"></param>
public void ScrollToElement(GUIComponent component)
{
GUI.PlayUISound(GUISoundType.Click);
SoundPlayer.PlayUISound(GUISoundType.Click);
List<GUIComponent> children = Content.Children.ToList();
int index = children.IndexOf(component);
if (index < 0) { return; }
@@ -17,7 +17,8 @@ namespace Barotrauma
public GUITextBox TextBox { get; private set; }
private GUIButton plusButton, minusButton;
public GUIButton PlusButton { get; private set; }
public GUIButton MinusButton { get; private set; }
private NumberType inputType;
public NumberType InputType
@@ -142,14 +143,14 @@ namespace Barotrauma
get => base.Enabled;
set
{
plusButton.Enabled = true;
minusButton.Enabled = true;
PlusButton.Enabled = true;
MinusButton.Enabled = true;
if (InputType == NumberType.Int) { ClampIntValue(); } else { ClampFloatValue(); }
TextBox.Enabled = value;
if (!value)
{
plusButton.Enabled = false;
minusButton.Enabled = false;
PlusButton.Enabled = false;
MinusButton.Enabled = false;
}
}
}
@@ -193,19 +194,19 @@ namespace Barotrauma
TextBox.OnTextChanged += TextChanged;
var buttonArea = new GUIFrame(new RectTransform(new Vector2(_relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight), style: null);
plusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), style: null);
GUI.Style.Apply(plusButton, "PlusButton", this);
plusButton.OnButtonDown += () =>
PlusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), style: null);
GUI.Style.Apply(PlusButton, "PlusButton", this);
PlusButton.OnButtonDown += () =>
{
pressedTimer = pressedDelay;
return true;
};
plusButton.OnClicked += (button, data) =>
PlusButton.OnClicked += (button, data) =>
{
IncreaseValue();
return true;
};
plusButton.OnPressed += () =>
PlusButton.OnPressed += () =>
{
if (!IsPressedTimerRunning)
{
@@ -214,19 +215,19 @@ namespace Barotrauma
return true;
};
minusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform, Anchor.BottomRight), style: null);
GUI.Style.Apply(minusButton, "MinusButton", this);
minusButton.OnButtonDown += () =>
MinusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform, Anchor.BottomRight), style: null);
GUI.Style.Apply(MinusButton, "MinusButton", this);
MinusButton.OnButtonDown += () =>
{
pressedTimer = pressedDelay;
return true;
};
minusButton.OnClicked += (button, data) =>
MinusButton.OnClicked += (button, data) =>
{
ReduceValue();
return true;
};
minusButton.OnPressed += () =>
MinusButton.OnPressed += () =>
{
if (!IsPressedTimerRunning)
{
@@ -279,17 +280,17 @@ namespace Barotrauma
private void HidePlusMinusButtons()
{
plusButton.Parent.Visible = false;
plusButton.Parent.IgnoreLayoutGroups = true;
PlusButton.Parent.Visible = false;
PlusButton.Parent.IgnoreLayoutGroups = true;
TextBox.RectTransform.RelativeSize = Vector2.One;
LayoutGroup.Recalculate();
}
private void ShowPlusMinusButtons()
{
plusButton.Parent.Visible = true;
plusButton.Parent.IgnoreLayoutGroups = false;
TextBox.RectTransform.RelativeSize = new Vector2(1.0f - plusButton.Parent.RectTransform.RelativeSize.X, 1.0f);
PlusButton.Parent.Visible = true;
PlusButton.Parent.IgnoreLayoutGroups = false;
TextBox.RectTransform.RelativeSize = new Vector2(1.0f - PlusButton.Parent.RectTransform.RelativeSize.X, 1.0f);
LayoutGroup.Recalculate();
}
@@ -369,12 +370,12 @@ namespace Barotrauma
if (MinValueFloat != null)
{
floatValue = Math.Max(floatValue, MinValueFloat.Value);
minusButton.Enabled = floatValue > MinValueFloat;
MinusButton.Enabled = floatValue > MinValueFloat;
}
if (MaxValueFloat != null)
{
floatValue = Math.Min(floatValue, MaxValueFloat.Value);
plusButton.Enabled = floatValue < MaxValueFloat;
PlusButton.Enabled = floatValue < MaxValueFloat;
}
}
@@ -390,8 +391,8 @@ namespace Barotrauma
intValue = Math.Min(intValue, MaxValueInt.Value);
UpdateText();
}
plusButton.Enabled = intValue < MaxValueInt;
minusButton.Enabled = intValue > MinValueInt;
PlusButton.Enabled = intValue < MaxValueInt;
MinusButton.Enabled = intValue > MinValueInt;
}
private void UpdateText()
@@ -31,7 +31,7 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce(
"GUIProgressBar.BarSize_setter",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Attempted to set the BarSize of a GUIProgressBar to an invalid value (" + value + ")\n" + Environment.StackTrace);
"Attempted to set the BarSize of a GUIProgressBar to an invalid value (" + value + ")\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
barSize = MathHelper.Clamp(value, 0.0f, 1.0f);
@@ -106,7 +106,7 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce(
"GUIProgressBar.Draw:GetProgress",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"ProgressGetter of a GUIProgressBar (" + ProgressGetter.Target.ToString() + " - " + ProgressGetter.Method.ToString() + ") returned an invalid value (" + newSize + ")\n" + Environment.StackTrace);
"ProgressGetter of a GUIProgressBar (" + ProgressGetter.Target.ToString() + " - " + ProgressGetter.Method.ToString() + ") returned an invalid value (" + newSize + ")\n" + Environment.StackTrace.CleanupStackTrace());
}
else
{
@@ -664,7 +664,7 @@ namespace Barotrauma
case (char)0x1: // ctrl-a
SelectAll();
break;
case (char)0x1A when !Readonly: // ctrl-z
case (char)0x1A when !Readonly && !SubEditorScreen.IsSubEditor(): // ctrl-z
text = memento.Undo();
if (text != Text)
{
@@ -674,7 +674,7 @@ namespace Barotrauma
OnTextChanged?.Invoke(this, Text);
}
break;
case (char)0x12 when !Readonly: // ctrl-r
case (char)0x12 when !Readonly && !SubEditorScreen.IsSubEditor(): // ctrl-r
text = memento.Redo();
if (text != Text)
{
@@ -519,7 +519,7 @@ namespace Barotrauma
{
if (displayErrors)
{
DebugConsole.ThrowError("Parent null" + Environment.StackTrace);
DebugConsole.ThrowError("Parent null" + Environment.StackTrace.CleanupStackTrace());
}
return false;
}
@@ -527,7 +527,7 @@ namespace Barotrauma
{
if (displayErrors)
{
DebugConsole.ThrowError("The children of the parent does not contain this child. This should not be possible! " + Environment.StackTrace);
DebugConsole.ThrowError("The children of the parent does not contain this child. This should not be possible! " + Environment.StackTrace.CleanupStackTrace());
}
return false;
}
@@ -535,7 +535,7 @@ namespace Barotrauma
{
if (displayErrors)
{
DebugConsole.ThrowError("Unable to remove the child from the parent. " + Environment.StackTrace);
DebugConsole.ThrowError("Unable to remove the child from the parent. " + Environment.StackTrace.CleanupStackTrace());
}
return false;
}
@@ -1,4 +1,5 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -31,9 +32,13 @@ namespace Barotrauma
private GUITextBlock shoppingCrateTotal;
private GUIButton clearAllButton, confirmButton;
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh;
private Point resolutionWhenCreated;
private bool hadPermissions;
private Dictionary<ItemPrefab, int> OwnedItems { get; } = new Dictionary<ItemPrefab, int>();
private CargoManager CargoManager => campaignUI.Campaign.CargoManager;
private Location CurrentLocation => campaignUI.Campaign.Map?.CurrentLocation;
private int PlayerMoney => campaignUI.Campaign.Money;
@@ -70,27 +75,30 @@ namespace Barotrauma
campaignUI.Campaign.Map.OnLocationChanged += UpdateLocation;
if (CurrentLocation?.Reputation != null)
{
CurrentLocation.Reputation.OnReputationValueChanged += Refresh;
CurrentLocation.Reputation.OnReputationValueChanged += () => { needsRefresh = true; };
}
campaignUI.Campaign.CargoManager.OnItemsInBuyCrateChanged += RefreshBuying;
campaignUI.Campaign.CargoManager.OnPurchasedItemsChanged += RefreshBuying;
campaignUI.Campaign.CargoManager.OnItemsInSellCrateChanged += RefreshSelling;
campaignUI.Campaign.CargoManager.OnItemsInBuyCrateChanged += () => { needsBuyingRefresh = true; };
campaignUI.Campaign.CargoManager.OnPurchasedItemsChanged += () => { needsBuyingRefresh = true; };
campaignUI.Campaign.CargoManager.OnItemsInSellCrateChanged += () => { needsSellingRefresh = true; };
campaignUI.Campaign.CargoManager.OnSoldItemsChanged += () =>
{
RefreshItemsToSell();
RefreshSelling();
needsItemsToSellRefresh = true;
needsSellingRefresh = true;
};
}
public void Refresh()
{
hadPermissions = HasPermissions;
RefreshBuying();
RefreshSelling();
UpdateOwnedItems();
RefreshBuying(updateOwned: false);
RefreshSelling(updateOwned: false);
needsRefresh = false;
}
private void RefreshBuying()
private void RefreshBuying(bool updateOwned = true)
{
if (updateOwned) { UpdateOwnedItems(); }
RefreshShoppingCrateBuyList();
//RefreshStoreDealsList();
RefreshStoreBuyList();
@@ -98,15 +106,18 @@ namespace Barotrauma
//storeDealsList.Enabled = hasPermissions;
storeBuyList.Enabled = hasPermissions;
shoppingCrateBuyList.Enabled = hasPermissions;
needsBuyingRefresh = false;
}
private void RefreshSelling()
private void RefreshSelling(bool updateOwned = true)
{
if (updateOwned) { UpdateOwnedItems(); }
RefreshShoppingCrateSellList();
RefreshStoreSellList();
var hasPermissions = HasPermissions;
storeSellList.Enabled = hasPermissions;
shoppingCrateSellList.Enabled = hasPermissions;
needsSellingRefresh = false;
}
private void CreateUI()
@@ -400,6 +411,7 @@ namespace Barotrauma
SetConfirmButtonBehavior();
clearAllButton = new GUIButton(new RectTransform(new Vector2(0.35f, 1.0f), buttonContainer.RectTransform), TextManager.Get("campaignstore.clearall"))
{
ClickSound = GUISoundType.DecreaseQuantity,
Enabled = HasPermissions,
ForceUpperCase = true,
OnClicked = (button, userData) =>
@@ -422,7 +434,7 @@ namespace Barotrauma
if (prevLocation?.Reputation != null)
{
prevLocation.Reputation.OnReputationValueChanged -= Refresh;
prevLocation.Reputation.OnReputationValueChanged = null;
}
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
@@ -432,7 +444,7 @@ namespace Barotrauma
ChangeStoreTab(StoreTab.Buy);
if (newLocation?.Reputation != null)
{
newLocation.Reputation.OnReputationValueChanged += Refresh;
newLocation.Reputation.OnReputationValueChanged += () => { needsRefresh = true; };
}
return;
}
@@ -535,6 +547,7 @@ namespace Barotrauma
{
(itemFrame.UserData as PurchasedItem).Quantity = quantity;
SetQuantityLabelText(StoreTab.Buy, itemFrame);
SetOwnedLabelText(itemFrame);
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0);
}
existingItemFrames.Add(itemFrame);
@@ -575,6 +588,7 @@ namespace Barotrauma
{
(itemFrame.UserData as PurchasedItem).Quantity = quantity;
SetQuantityLabelText(StoreTab.Sell, itemFrame);
SetOwnedLabelText(itemFrame);
SetItemFrameStatus(itemFrame, hasPermissions);
}
if (quantity < 1) { itemFrame.Visible = false; }
@@ -617,6 +631,7 @@ namespace Barotrauma
CargoManager.ModifyItemQuantityInSellCrate(crateItem.ItemPrefab, playerItemQuantity - crateItem.Quantity);
}
}
needsItemsToSellRefresh = false;
}
private void RefreshShoppingCrateList(List<PurchasedItem> items, GUIListBox listBox)
@@ -645,6 +660,7 @@ namespace Barotrauma
numInput.UserData = item;
numInput.Enabled = hasPermissions;
}
SetOwnedLabelText(itemFrame);
SetItemFrameStatus(itemFrame, hasPermissions);
}
existingItemFrames.Add(itemFrame);
@@ -740,7 +756,7 @@ namespace Barotrauma
tooltip += "\n" + pi.ItemPrefab.Description;
}
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 60)), parent: listBox.Content.RectTransform), style: "ListBoxElement")
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 80)), parent: listBox.Content.RectTransform), style: "ListBoxElement")
{
ToolTip = tooltip,
UserData = pi
@@ -775,7 +791,7 @@ namespace Barotrauma
CanBeFocused = false,
Stretch = true
};
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndQuantityGroup.RectTransform),
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), nameAndQuantityGroup.RectTransform),
pi.ItemPrefab.Name, font: GUI.SubHeadingFont, textAlignment: Alignment.BottomLeft)
{
CanBeFocused = false,
@@ -783,11 +799,12 @@ namespace Barotrauma
TextScale = 0.85f,
UserData = "name"
};
GUILayoutGroup shoppingCrateAmountGroup = null;
GUINumberInput amountInput = null;
if (listBox == storeBuyList || listBox == storeSellList)
{
var block = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndQuantityGroup.RectTransform),
CreateQuantityLabelText(listBox == storeSellList ? StoreTab.Sell : StoreTab.Buy, pi.Quantity), font: GUI.Font, textAlignment: Alignment.TopLeft)
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), nameAndQuantityGroup.RectTransform),
CreateQuantityLabelText(listBox == storeSellList ? StoreTab.Sell : StoreTab.Buy, pi.Quantity), font: GUI.Font, textAlignment: Alignment.BottomLeft)
{
CanBeFocused = false,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
@@ -797,7 +814,13 @@ namespace Barotrauma
}
else if (listBox == shoppingCrateBuyList || listBox == shoppingCrateSellList)
{
amountInput = new GUINumberInput(new RectTransform(new Vector2(0.5f), nameAndQuantityGroup.RectTransform), GUINumberInput.NumberType.Int)
var relativePadding = nameBlock.Padding.X / nameBlock.Rect.Width;
shoppingCrateAmountGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - relativePadding, 0.6f), nameAndQuantityGroup.RectTransform) { RelativeOffset = new Vector2(relativePadding, 0) },
isHorizontal: true)
{
RelativeSpacing = 0.02f
};
amountInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), shoppingCrateAmountGroup.RectTransform), GUINumberInput.NumberType.Int)
{
MinValueInt = 0,
MaxValueInt = GetMaxAvailable(pi.ItemPrefab, listBox == shoppingCrateBuyList ? StoreTab.Buy : StoreTab.Sell),
@@ -818,9 +841,25 @@ namespace Barotrauma
}
AddToShoppingCrate(purchasedItem, quantity: numberInput.IntValue - purchasedItem.Quantity);
};
amountInput.PlusButton.ClickSound = GUISoundType.IncreaseQuantity;
amountInput.MinusButton.ClickSound = GUISoundType.DecreaseQuantity;
frame.HoverColor = frame.SelectedColor = Color.Transparent;
}
// Amount in players' inventories and on the sub
var rectTransform = shoppingCrateAmountGroup == null ?
new RectTransform(new Vector2(1.0f, 0.3f), nameAndQuantityGroup.RectTransform) :
new RectTransform(new Vector2(0.6f, 1.0f), shoppingCrateAmountGroup.RectTransform);
new GUITextBlock(rectTransform, CreateOwnedLabelText(OwnedItems.GetValueOrDefault(pi.ItemPrefab, 0)), font: GUI.Font,
textAlignment: shoppingCrateAmountGroup == null ? Alignment.TopLeft : Alignment.CenterLeft)
{
CanBeFocused = false,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
TextScale = 0.85f,
UserData = "owned"
};
shoppingCrateAmountGroup?.Recalculate();
var buttonRelativeWidth = (0.9f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
var priceBlock = new GUITextBlock(new RectTransform(new Vector2(priceAndButtonRelativeWidth - buttonRelativeWidth, 1.0f), mainGroup.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
@@ -842,6 +881,7 @@ namespace Barotrauma
{
new GUIButton(new RectTransform(new Vector2(buttonRelativeWidth, 0.9f), mainGroup.RectTransform), style: "StoreAddToCrateButton")
{
ClickSound = GUISoundType.IncreaseQuantity,
Enabled = !forceDisable && pi.Quantity > 0,
ForceUpperCase = true,
UserData = "addbutton",
@@ -852,6 +892,7 @@ namespace Barotrauma
{
new GUIButton(new RectTransform(new Vector2(buttonRelativeWidth, 0.9f), mainGroup.RectTransform), style: "StoreRemoveFromCrateButton")
{
ClickSound = GUISoundType.DecreaseQuantity,
Enabled = !forceDisable,
ForceUpperCase = true,
UserData = "removebutton",
@@ -869,6 +910,39 @@ namespace Barotrauma
return frame;
}
private void UpdateOwnedItems()
{
OwnedItems.Clear();
// Add items on the sub(s)
Submarine.MainSub?.GetItems(true)
.Where(i => i.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached) &&
i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null)))
.ForEach(i => AddToOwnedItems(i.Prefab));
// Add items in character inventories
foreach (Character c in GameMain.GameSession.CrewManager.GetCharacters())
{
Item.ItemList.Where(i => i != null && i.GetRootInventoryOwner() == c)
.ForEach(i => AddToOwnedItems(i.Prefab));
}
// Add items already purchased
CargoManager?.PurchasedItems?.ForEach(pi => AddToOwnedItems(pi.ItemPrefab, amount: pi.Quantity));
void AddToOwnedItems(ItemPrefab itemPrefab, int amount = 1)
{
if (OwnedItems.ContainsKey(itemPrefab))
{
OwnedItems[itemPrefab] += amount;
}
else
{
OwnedItems.Add(itemPrefab, amount);
}
}
}
private void SetItemFrameStatus(GUIComponent itemFrame, bool enabled)
{
if (itemFrame == null || !(itemFrame.UserData is PurchasedItem pi)) { return; }
@@ -901,6 +975,11 @@ namespace Barotrauma
numberInput.Enabled = enabled;
}
if (itemFrame.FindChild("owned", recursive: true) is GUITextBlock owned)
{
owned.TextColor = color;
}
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock price)
{
price.TextColor = color;
@@ -918,7 +997,8 @@ namespace Barotrauma
private void SetQuantityLabelText(StoreTab mode, GUIComponent itemFrame)
{
if (itemFrame?.FindChild("quantitylabel", recursive: true) is GUITextBlock label)
if (itemFrame == null) { return; }
if (itemFrame.FindChild("quantitylabel", recursive: true) is GUITextBlock label)
{
label.Text = CreateQuantityLabelText(mode, (itemFrame.UserData as PurchasedItem).Quantity);
}
@@ -928,6 +1008,23 @@ namespace Barotrauma
TextManager.GetWithVariable("campaignstore.quantity", "[amount]", quantity.ToString()) :
TextManager.GetWithVariable("campaignstore.instock", "[amount]", quantity.ToString());
private void SetOwnedLabelText(GUIComponent itemComponent)
{
if (itemComponent == null) { return; }
var itemCount = 0;
if (itemComponent.UserData is PurchasedItem pi)
{
itemCount = OwnedItems.GetValueOrDefault(pi.ItemPrefab, itemCount);
}
if (itemComponent.FindChild("owned", recursive: true) is GUITextBlock label)
{
label.Text = CreateOwnedLabelText(itemCount);
}
}
private string CreateOwnedLabelText(int itemCount) => itemCount > 0 ?
TextManager.GetWithVariable("campaignstore.owned", "[amount]", itemCount.ToString()) : "";
private int GetMaxAvailable(ItemPrefab itemPrefab, StoreTab mode)
{
var list = mode == StoreTab.Sell ? itemsToSell : CurrentLocation.StoreStock;
@@ -1103,11 +1200,12 @@ namespace Barotrauma
if (GameMain.GraphicsWidth != resolutionWhenCreated.X || GameMain.GraphicsHeight != resolutionWhenCreated.Y)
{
CreateUI();
needsRefresh = false;
}
else if (hadPermissions != HasPermissions)
{
Refresh();
}
if (needsRefresh || hadPermissions != HasPermissions) { Refresh(); }
if (needsBuyingRefresh) { RefreshBuying(); }
if (needsItemsToSellRefresh) { RefreshItemsToSell(); }
if (needsSellingRefresh) { RefreshSelling(); }
}
}
}
@@ -491,7 +491,7 @@ namespace Barotrauma
new GUITextBlock(rectT(1, 0, textLayout), title, font: GUI.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true };
new GUITextBlock(rectT(1, 0, textLayout), FormatCurrency(price));
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, contentLayout), childAnchor: Anchor.Center) { UserData = "buybutton" };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { Enabled = Campaign.Money >= price && !isDisabled, OnClicked = onPressed };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { ClickSound = GUISoundType.HireRepairClick, Enabled = Campaign.Money >= price && !isDisabled, OnClicked = onPressed };
contentLayout.Recalculate();
buyButtonLayout.Recalculate();
@@ -79,7 +79,7 @@ namespace Barotrauma
if (gameSession == value) { return; }
if (value == null && Screen.Selected == GameScreen && gameSession.GameMode is CampaignMode)
{
DebugConsole.AddWarning("GameSession set to null while in the game screen\n" + Environment.StackTrace);
DebugConsole.AddWarning("GameSession set to null while in the game screen\n" + Environment.StackTrace.CleanupStackTrace());
}
if (gameSession?.GameMode != null && gameSession.GameMode != value?.GameMode)
{
@@ -669,7 +669,7 @@ namespace Barotrauma
#if DEBUG
DebugConsole.ThrowError($"Failed to parse a Steam friend's connect invitation command ({connectCommand})", e);
#else
DebugConsole.Log($"Failed to parse a Steam friend's connect invitation command ({connectCommand})\n" + e.StackTrace);
DebugConsole.Log($"Failed to parse a Steam friend's connect invitation command ({connectCommand})\n" + e.StackTrace.CleanupStackTrace());
#endif
ConnectName = null;
ConnectEndpoint = null;
@@ -52,11 +52,11 @@ namespace Barotrauma
i.GetRootInventoryOwner() == character &&
!i.SpawnedInOutpost &&
(i.ContainedItems == null || i.ContainedItems.None() || i.ContainedItems.All(ci => soldEntities.Any(se => se.Item == ci))) &&
i.Condition >= 0.9f * i.MaxCondition && soldEntities.None(se => se.Item == i));
(i.Condition >= 0.9f * i.MaxCondition || i.Prefab.AllowSellingWhenBroken) && soldEntities.None(se => se.Item == i));
// Prevent selling items in equipment slots
var slots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card };
foreach (InvSlotType slot in slots)
var equipmentSlots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card };
foreach (InvSlotType slot in equipmentSlots)
{
var index = character.Inventory.FindLimbSlot(slot);
if (character.Inventory.Items[index] is Item item)
@@ -69,18 +69,27 @@ namespace Barotrauma
}
}
// Prevent selling items contained in certain equipped items (like battery cell in equipped headset or oxygen tank in equipped diving mask)
slots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.OuterClothes, InvSlotType.Headset };
foreach (InvSlotType slot in slots)
// Prevent selling items contained inside equipped items
foreach (InvSlotType slot in equipmentSlots)
{
var index = character.Inventory.FindLimbSlot(slot);
if (character.Inventory.Items[index] is Item item &&
item.ContainedItems != null && item.AllowedSlots.Contains(InvSlotType.Any))
{
foreach (Item containedItem in item.ContainedItems)
RemoveContainedFromSellables(item);
}
}
void RemoveContainedFromSellables(Item item)
{
foreach (Item containedItem in item.ContainedItems)
{
if (containedItem == null) { continue; }
if (containedItem.ContainedItems != null)
{
sellables.Remove(containedItem);
RemoveContainedFromSellables(containedItem);
}
sellables.Remove(containedItem);
}
}
@@ -27,7 +27,7 @@ namespace Barotrauma
private GUIListBox crewList;
private GUIButton commandButton, toggleCrewButton;
private float crewListOpenState;
private bool toggleCrewListOpen = true;
private bool _isCrewMenuOpen = true;
private Point crewListEntrySize;
private GUIFrame contextMenu;
@@ -42,16 +42,26 @@ namespace Barotrauma
public bool AllowCharacterSwitch = true;
public bool ToggleCrewListOpen
/// <summary>
/// This property stores the preference in settings. Don't use for automatic logic.
/// Use AutoShowCrewList(), AutoHideCrewList(), and ResetCrewList().
/// </summary>
public bool IsCrewMenuOpen
{
get { return toggleCrewListOpen; }
get { return _isCrewMenuOpen; }
set
{
if (toggleCrewListOpen == value) { return; }
toggleCrewListOpen = GameMain.Config.CrewMenuOpen = value;
if (_isCrewMenuOpen == value) { return; }
_isCrewMenuOpen = GameMain.Config.CrewMenuOpen = value;
}
}
public bool AutoShowCrewList() => _isCrewMenuOpen = true;
public void AutoHideCrewList() => _isCrewMenuOpen = false;
public void ResetCrewList() => _isCrewMenuOpen = GameMain.Config.CrewMenuOpen;
const float CommandNodeAnimDuration = 0.2f;
public List<GUIButton> OrderOptionButtons = new List<GUIButton>();
@@ -139,7 +149,7 @@ namespace Barotrauma
{
OnClicked = (GUIButton btn, object userdata) =>
{
ToggleCrewListOpen = !ToggleCrewListOpen;
IsCrewMenuOpen = !IsCrewMenuOpen;
return true;
}
};
@@ -286,7 +296,7 @@ namespace Barotrauma
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
prevUIScale = GUI.Scale;
ToggleCrewListOpen = GameMain.Config.CrewMenuOpen;
_isCrewMenuOpen = GameMain.Config.CrewMenuOpen;
dismissedOrderPrefab ??= Order.GetPrefab("dismissed");
}
@@ -318,7 +328,7 @@ namespace Barotrauma
{
if (character == null)
{
DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace);
DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
characters.Remove(character);
@@ -567,7 +577,7 @@ namespace Barotrauma
{
if (!IsSinglePlayer)
{
DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace);
DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
if (string.IsNullOrEmpty(text)) { return; }
@@ -583,7 +593,7 @@ namespace Barotrauma
{
if (!IsSinglePlayer)
{
DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace);
DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
if (string.IsNullOrEmpty(message.Text)) { return; }
@@ -675,8 +685,7 @@ namespace Barotrauma
AddOrder(new Order(order.Prefab ?? order, hull, null, orderGiver), order.FadeOutTime);
if (IsSinglePlayer)
{
orderGiver.Speak(
order.GetChatMessage("", hull.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order);
orderGiver.Speak(order.GetChatMessage("", hull.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order);
}
else
{
@@ -692,12 +701,11 @@ namespace Barotrauma
if (IsSinglePlayer)
{
character.SetOrder(order, option, orderGiver, speak: orderGiver != character);
orderGiver?.Speak(
order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null);
orderGiver?.Speak(order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null);
}
else if (orderGiver != null)
{
OrderChatMessage msg = new OrderChatMessage(order, option, order?.TargetEntity ?? order?.TargetItemComponent?.Item, character, orderGiver);
OrderChatMessage msg = new OrderChatMessage(order, option, order?.TargetSpatialEntity ?? order?.TargetItemComponent?.Item as ISpatialEntity, character, orderGiver);
GameMain.Client?.SendChatMessage(msg);
}
}
@@ -1290,7 +1298,7 @@ namespace Barotrauma
{
CreateCommandUI(HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) ? Character.Controlled : GUI.MouseOn?.UserData as Character);
}
GUI.PlayUISound(GUISoundType.PopupMenu);
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
clicklessSelectionActive = isOpeningClick = true;
}
@@ -1524,14 +1532,15 @@ namespace Barotrauma
new Vector2(-crewArea.Rect.Width - HUDLayoutSettings.Padding, 0.0f),
Vector2.Zero,
crewListOpenState).ToPoint();
crewListOpenState = ToggleCrewListOpen ?
crewListOpenState = IsCrewMenuOpen ?
Math.Min(crewListOpenState + deltaTime * 2.0f, 1.0f) :
Math.Max(crewListOpenState - deltaTime * 2.0f, 0.0f);
if (GUI.KeyboardDispatcher.Subscriber == null && PlayerInput.KeyHit(InputType.CrewOrders))
{
GUI.PlayUISound(GUISoundType.PopupMenu);
ToggleCrewListOpen = !ToggleCrewListOpen;
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
IsCrewMenuOpen = !IsCrewMenuOpen;
}
}
@@ -1589,8 +1598,8 @@ namespace Barotrauma
private Hull hullContext;
private bool isContextual;
private readonly List<Order> contextualOrders = new List<Order>();
private Point shortcutCenterNodeOffset;
private const int maxShortcutNodeCount = 4;
private Point shorcutCenterNodeOffset;
private const int maxShortCutNodeCount = 4;
private bool WasCommandInterfaceDisabledThisUpdate { get; set; }
private bool CanIssueOrders
@@ -1773,7 +1782,7 @@ namespace Barotrauma
returnNodeMargin = returnNodeSize.X * 0.5f;
nodeDistance = (int)(150 * GUI.Scale);
shortcutCenterNodeOffset = new Point(0, (int)(1.25f * nodeDistance));
shorcutCenterNodeOffset = new Point(0, (int)(1.25f * nodeDistance));
}
private List<OrderCategory> GetAvailableCategories()
@@ -2057,7 +2066,7 @@ namespace Barotrauma
shortcutNodes.Clear();
if (shortcutNodes.Count < maxShortcutNodeCount && sub.GetItems(false).Find(i => i.HasTag("reactor") && !i.NonInteractable)?.GetComponent<Reactor>() is Reactor reactor)
if (shortcutNodes.Count < maxShortCutNodeCount && sub.GetItems(false).Find(i => i.HasTag("reactor") && !i.NonInteractable)?.GetComponent<Reactor>() is Reactor reactor)
{
var reactorOutput = -reactor.CurrPowerConsumption;
// If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor
@@ -2075,7 +2084,7 @@ namespace Barotrauma
// TODO: Reconsider the conditions as bot captain can have the nav term selected without operating it
// If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up
// --> Create shortcut node for Steer order
if (shortcutNodes.Count < maxShortcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("captain")) &&
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("captain")) &&
sub.GetItems(false).Find(i => i.HasTag("navterminal") && !i.NonInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) &&
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
{
@@ -2085,7 +2094,7 @@ namespace Barotrauma
// If player is not a security officer AND invaders are reported
// --> Create shorcut node for Fight Intruders order
if (shortcutNodes.Count < maxShortcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("securityofficer")) &&
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("securityofficer")) &&
(Order.GetPrefab("reportintruders") is Order reportIntruders && ActiveOrders.Any(o => o.First.Prefab == reportIntruders)))
{
shortcutNodes.Add(
@@ -2094,7 +2103,7 @@ namespace Barotrauma
// If player is not a mechanic AND a breach has been reported
// --> Create shorcut node for Fix Leaks order
if (shortcutNodes.Count < maxShortcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("mechanic")) &&
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("mechanic")) &&
(Order.GetPrefab("reportbreach") is Order reportBreach && ActiveOrders.Any(o => o.First.Prefab == reportBreach)))
{
shortcutNodes.Add(
@@ -2103,7 +2112,7 @@ namespace Barotrauma
// If player is not an engineer AND broken devices have been reported
// --> Create shortcut node for Repair Damaged Systems order
if (shortcutNodes.Count < maxShortcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("engineer")) &&
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("engineer")) &&
(Order.GetPrefab("reportbrokendevices") is Order reportBrokenDevices && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices)))
{
shortcutNodes.Add(
@@ -2112,13 +2121,13 @@ namespace Barotrauma
// If fire is reported
// --> Create shortcut node for Extinguish Fires order
if (shortcutNodes.Count < maxShortcutNodeCount && ActiveOrders.Any(o=> o.First.Prefab == Order.GetPrefab("reportfire")))
if (shortcutNodes.Count < maxShortCutNodeCount && ActiveOrders.Any(o=> o.First.Prefab == Order.GetPrefab("reportfire")))
{
shortcutNodes.Add(
CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab("extinguishfires"), -1));
}
if (shortcutNodes.Count < maxShortcutNodeCount && characterContext?.Info?.Job?.Prefab?.AppropriateOrders != null)
if (shortcutNodes.Count < maxShortCutNodeCount && characterContext?.Info?.Job?.Prefab?.AppropriateOrders != null)
{
foreach (string orderIdentifier in characterContext.Info.Job.Prefab.AppropriateOrders)
{
@@ -2131,11 +2140,17 @@ namespace Barotrauma
{
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, orderPrefab, -1));
}
if (shortcutNodes.Count >= maxShortcutNodeCount) { break; }
if (shortcutNodes.Count >= maxShortCutNodeCount) { break; }
}
}
}
if (shortcutNodes.Count < maxShortCutNodeCount && characterContext != null && !characterContext.IsDismissed)
{
shortcutNodes.Add(
CreateOrderNode(shortcutNodeSize, null, Point.Zero, dismissedOrderPrefab, -1));
}
if (shortcutNodes.Count < 1) { return; }
shortcutCenterNode = new GUIButton(
@@ -2148,7 +2163,7 @@ namespace Barotrauma
c.PressedColor = c.Color;
c.SelectedColor = c.Color;
}
shortcutCenterNode.RectTransform.MoveOverTime(shortcutCenterNodeOffset, CommandNodeAnimDuration);
shortcutCenterNode.RectTransform.MoveOverTime(shorcutCenterNodeOffset, CommandNodeAnimDuration);
var nodeCountForCalculations = shortcutNodes.Count * 2 + 2;
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, 0.75f * nodeDistance, nodeCountForCalculations);
@@ -2156,7 +2171,7 @@ namespace Barotrauma
for (int i = 0; i < shortcutNodes.Count; i++)
{
shortcutNodes[i].RectTransform.Parent = commandFrame.RectTransform;
shortcutNodes[i].RectTransform.MoveOverTime(shortcutCenterNodeOffset + offsets[firstOffsetIndex - i].ToPoint(), CommandNodeAnimDuration);
shortcutNodes[i].RectTransform.MoveOverTime(shorcutCenterNodeOffset + offsets[firstOffsetIndex - i].ToPoint(), CommandNodeAnimDuration);
}
}
@@ -2171,7 +2186,7 @@ namespace Barotrauma
{
order = orders[i];
disableNode = !CanSomeoneHearCharacter() ||
(order.MustSetTarget && (order.ItemComponentType != null || order.ItemIdentifiers.Length > 0) && order.GetMatchingItems(true).None());
(order.MustSetTarget && (order.ItemComponentType != null || order.TargetItems.Length > 0) && order.GetMatchingItems(true).None());
optionNodes.Add(new Tuple<GUIComponent, Keys>(
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), order, (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false),
!disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None));
@@ -2190,13 +2205,14 @@ namespace Barotrauma
// Check if targeting an item or a hull
if (itemContext != null && !itemContext.NonInteractable)
{
ItemComponent targetComponent;
foreach (Order p in Order.PrefabList)
{
if ((p.ItemIdentifiers.Length > 0 && (p.ItemIdentifiers.Contains(itemContext.Prefab.Identifier) || itemContext.HasTag(p.ItemIdentifiers))) ||
(p.ItemComponentType != null && itemContext.Components.Any(c => c?.GetType() == p.ItemComponentType)))
targetComponent = null;
if ((p.TargetItems.Length > 0 && (p.TargetItems.Contains(itemContext.Prefab.Identifier) || itemContext.HasTag(p.TargetItems))) ||
p.TryGetTargetItemComponent(itemContext, out targetComponent))
{
contextualOrders.Add(p.HasOptions ? p :
new Order(p, itemContext, itemContext.Components.FirstOrDefault(c => c?.GetType() == p.ItemComponentType), Character.Controlled));
contextualOrders.Add(p.HasOptions ? p : new Order(p, itemContext, targetComponent, Character.Controlled));
}
}
@@ -2205,8 +2221,8 @@ namespace Barotrauma
var operateWeaponsPrefab = Order.GetPrefab(orderIdentifier);
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Components.Any(c => c is Controller))
{
var turret = itemContext.GetConnectedComponents<Turret>().FirstOrDefault(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier)) ??
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier));
var turret = itemContext.GetConnectedComponents<Turret>().FirstOrDefault(c => operateWeaponsPrefab.TargetItems.Contains(c.Item.Prefab.Identifier)) ??
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => operateWeaponsPrefab.TargetItems.Contains(c.Item.Prefab.Identifier));
if (turret != null) { contextualOrders.Add(new Order(operateWeaponsPrefab, turret.Item, turret, Character.Controlled)); }
}
@@ -2214,14 +2230,17 @@ namespace Barotrauma
orderIdentifier = "repairsystems";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Repairables.Any(r => itemContext.ConditionPercentage < r.RepairThreshold))
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, null, Character.Controlled));
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, null, Character.Controlled));
contextualOrders.Add(new Order(Order.GetPrefab("repairelectrical"), itemContext, targetItem: null, Character.Controlled));
}
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, null, Character.Controlled));
contextualOrders.Add(new Order(Order.GetPrefab("repairmechanical"), itemContext, targetItem: null, Character.Controlled));
}
else
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled));
}
}
@@ -2235,23 +2254,29 @@ namespace Barotrauma
if (contextualOrders.None())
{
// If there are other crew members alive and there are no other contextual orders available, show the 'wait' order
orderIdentifier = "wait";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && characters.Any(c => c != Character.Controlled))
orderIdentifier = "cleanupitems";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled))
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, null, Character.Controlled));
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled));
}
}
}
else if(hullContext != null)
else if (hullContext != null)
{
contextualOrders.Add(new Order(Order.GetPrefab("fixleaks"), hullContext, null, Character.Controlled));
contextualOrders.Add(new Order(Order.GetPrefab("fixleaks"), hullContext, targetItem: null, Character.Controlled));
}
if (characters.Any(c => c != Character.Controlled))
if (contextualOrders.None(o => o.Category != OrderCategory.Movement))
{
// Show 'follow' order only when there are no orders of other categories available
if (contextualOrders.None(o => o.Category != OrderCategory.Movement))
orderIdentifier = "wait";
if (contextualOrders.None(o => o.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));
}
if (characters.Any(c => c != Character.Controlled))
{
orderIdentifier = "follow";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
@@ -2259,14 +2284,14 @@ namespace Barotrauma
contextualOrders.Add(Order.GetPrefab(orderIdentifier));
}
}
// Show 'dismissed' order only when there are crew members with active orders
orderIdentifier = "dismissed";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) &&
characters.Any(c => c.CurrentOrder != null && !c.CurrentOrder.Identifier.Equals(orderIdentifier)))
{
contextualOrders.Add(Order.GetPrefab(orderIdentifier));
}
}
// 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.CurrentOrder != null && !c.CurrentOrder.Identifier.Equals(orderIdentifier)))
{
contextualOrders.Add(Order.GetPrefab(orderIdentifier));
}
}
@@ -2280,17 +2305,19 @@ namespace Barotrauma
}
}
// 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.ItemIdentifiers.Length > 0 && o.ItemIdentifiers.Contains(item.Prefab.Identifier))) { return true; }
if (Order.PrefabList.Any(o => item.HasTag(o.ItemIdentifiers))) { return true; }
if (Order.PrefabList.Any(o => o.ItemComponentType != null && item.Components.Any(c => c?.GetType() == o.ItemComponentType))) { return true; }
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.TryGetTargetItemComponent(item, out _))) { return true; }
if (AIObjectiveCleanupItems.IsValidTarget(item, Character.Controlled)) { return true; }
if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold)) { return true; }
var operateWeaponsPrefab = Order.GetPrefab("operateweapons");
return item.Components.Any(c => c is Controller) &&
(item.GetConnectedComponents<Turret>().Any(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier)) ||
item.GetConnectedComponents<Turret>(recursive: true).Any(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier)));
(item.GetConnectedComponents<Turret>().Any(c => operateWeaponsPrefab.TargetItems.Contains(c.Item.Prefab.Identifier)) ||
item.GetConnectedComponents<Turret>(recursive: true).Any(c => operateWeaponsPrefab.TargetItems.Contains(c.Item.Prefab.Identifier)));
}
private GUIButton CreateOrderNode(Point size, RectTransform parent, Point offset, Order order, int hotkey, bool disableNode = false, bool checkIfOrderCanBeHeard = true)
@@ -2462,7 +2489,7 @@ namespace Barotrauma
style: "GUITextBox")
{
UserData = new Tuple<Order, string>(
item == null ? order : new Order(order, item, item.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType)),
item == null ? order : new Order(order, item, order.GetTargetItemComponent(item)),
order.Options[i]),
Font = GUI.SmallFont,
OnClicked = (_, userData) =>
@@ -2479,7 +2506,7 @@ namespace Barotrauma
}
else
{
var userData = new Tuple<Order, string>(item == null ? order : new Order(order, item, item.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType)), "");
var userData = new Tuple<Order, string>(item == null ? order : new Order(order, item, order.GetTargetItemComponent(item)), "");
optionElement = new GUIButton(
new RectTransform(
new Point((int)(50 * GUI.Scale)),
@@ -2528,7 +2555,7 @@ namespace Barotrauma
var item = itemContext != null ?
(order.UseController ? itemContext.GetConnectedComponents<Turret>().FirstOrDefault()?.Item ?? itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault()?.Item : itemContext) :
(matchingItems.Count > 0 ? matchingItems[0] : null);
var o = item == null || !order.IsPrefab ? order : new Order(order, item, item.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType));
var o = item == null || !order.IsPrefab ? order : new Order(order, item, order.GetTargetItemComponent(item));
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance,
GetCircumferencePointCount(order.Options.Length),
GetFirstNodeAngle(order.Options.Length));
@@ -2754,7 +2781,7 @@ namespace Barotrauma
// Order icon
GUIImage orderIcon;
if (character.CurrentOrder != null && !character.CurrentOrder.Identifier.Equals("dismissed"))
if (!character.IsDismissed)
{
orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), character.CurrentOrder.SymbolSprite, scaleToFit: true);
var tooltip = character.CurrentOrder.Name;
@@ -2910,7 +2937,7 @@ namespace Barotrauma
{
bearing = GetBearing(
centerNode.RectTransform.AnimTargetPos.ToVector2(),
shortcutCenterNodeOffset.ToVector2());
shorcutCenterNodeOffset.ToVector2());
}
return nodeCount % 2 > 0 ?
MathHelper.ToRadians(bearing + 360.0f / nodeCount / 2) :
@@ -2997,8 +3024,7 @@ namespace Barotrauma
#endif
if (order.Identifier == dismissedOrderPrefab.Identifier)
{
return characters.FindAll(c => c.CurrentOrder != null && c.CurrentOrder.Identifier != dismissedOrderPrefab.Identifier)
.OrderBy(c => c.Info.DisplayName).ToList();
return characters.FindAll(c => !c.IsDismissed).OrderBy(c => c.Info.DisplayName).ToList();
}
return GetCharactersSortedForOrder(order, order.Identifier != "follow").ToList();
}
@@ -3006,16 +3032,16 @@ namespace Barotrauma
private IEnumerable<Character> GetCharactersSortedForOrder(Order order, bool includeSelf)
{
return characters.FindAll(c => Character.Controlled == null || ((includeSelf || c != Character.Controlled) && c.TeamID == Character.Controlled.TeamID))
// 1. Prioritize those who are already ordered to operate the item target of the new 'operate' order
.OrderByDescending(c => c.CurrentOrder != null && order.Category == OrderCategory.Operate && c.CurrentOrder.Identifier == order.Identifier && c.CurrentOrder.TargetEntity == order.TargetEntity)
// 2. Prioritize those who are currently dismissed
.ThenByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier)
// 3. Prioritize those who are not currently assigned with the same type of order (for example, when giving a 'Fix Leak' order, prioritize those who have a different order)
.ThenBy(c => c.CurrentOrder != null && c.CurrentOrder.Identifier == order.Identifier && c.CurrentOrder.TargetEntity == order.TargetEntity)
// 4. Prioritize those with the appropriate job for the order
// 1. Prioritize those who are on the same submarine than the controlled character
.OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine)
// 2. Prioritize those who are already ordered to operate the item target of the new 'operate' order, or given the same maintenance order as now issued
.ThenByDescending(c => c.CurrentOrder != null && c.CurrentOrder.Identifier == order.Identifier && (order.Category == OrderCategory.Maintenance || (order.Category == OrderCategory.Operate && c.CurrentOrder.TargetSpatialEntity == order.TargetSpatialEntity)))
// 3. Prioritize those with the appropriate job for the order
.ThenByDescending(c => order.HasAppropriateJob(c))
// 5. Prioritize those with the lowest "weight" of the current order
.ThenBy(c => c.CurrentOrder?.Weight)
// 4. Prioritize bots over player controlled characters
.ThenByDescending(c => c.IsBot)
// 5. Use the priority value of the current objective
.ThenBy(c => c.AIController?.ObjectiveManager.CurrentObjective?.Priority)
// 6. Prioritize those with the best skill for the order
.ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
}
@@ -413,8 +413,6 @@ namespace Barotrauma
//--------------------------------------
bool save = false;
if (success)
{
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
@@ -161,7 +161,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!captain_medicObjectiveSensor.MotionDetected);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(captain_medic.Info.DisplayName, TextManager.Get("Captain.Radio.Medic"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false);
GameMain.GameSession.CrewManager.ToggleCrewListOpen = true;
GameMain.GameSession.CrewManager.AutoShowCrewList();
GameMain.GameSession.CrewManager.AddCharacter(captain_medic);
TriggerTutorialSegment(0, GameMain.Config.KeyBindText(InputType.Command));
do
@@ -275,7 +275,7 @@ namespace Barotrauma.Tutorials
GameMain.GameSession.CrewManager.AllowCharacterSwitch = false;
GameMain.GameSession.CrewManager.AddCharacter(doctor);
GameMain.GameSession.CrewManager.AddCharacter(patient1);
GameMain.GameSession.CrewManager.ToggleCrewListOpen = true;
GameMain.GameSession.CrewManager.AutoShowCrewList();
patient1.CharacterHealth.UseHealthWindow = false;
yield return new WaitForSeconds(3.0f, false);
@@ -87,9 +87,9 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
engineer = Character.Controlled;
var toolbox = FindOrGiveItem(engineer, "toolbox");
toolbox.Unequip(engineer);
engineer.Inventory.RemoveItem(toolbox);
var toolbelt = FindOrGiveItem(engineer, "toolbelt");
toolbelt.Unequip(engineer);
engineer.Inventory.RemoveItem(toolbelt);
var repairOrder = Order.GetPrefab("repairsystems");
engineer_repairIcon = repairOrder.SymbolSprite;
@@ -87,9 +87,9 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
mechanic = Character.Controlled;
var toolbox = FindOrGiveItem(mechanic, "toolbox");
toolbox.Unequip(mechanic);
mechanic.Inventory.RemoveItem(toolbox);
var toolbelt = FindOrGiveItem(mechanic, "toolbelt");
toolbelt.Unequip(mechanic);
mechanic.Inventory.RemoveItem(toolbelt);
var crowbar = FindOrGiveItem(mechanic, "crowbar");
crowbar.Unequip(mechanic);
@@ -584,8 +584,8 @@ namespace Barotrauma.Tutorials
}
infoBlock.RectTransform.NonScaledSize = new Point(infoBlock.Rect.Width, (int)(infoContent.Children.Sum(c => c.Rect.Height + infoContent.AbsoluteSpacing) / infoContent.RectTransform.RelativeSize.Y));
GUI.PlayUISound(GUISoundType.UIMessage);
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
return background;
}
@@ -1149,7 +1149,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);
GameAnalyticsManager.AddErrorEventOnce("SetVoiceCaptureMode", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, "Failed to set voice capture mode. " + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
VoiceSetting = VoiceMode.Disabled;
}
@@ -51,6 +51,7 @@ namespace Barotrauma
limbSlotIcons.Add(InvSlotType.LeftHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(634, 0, 128, 128)));
limbSlotIcons.Add(InvSlotType.RightHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(762, 0, 128, 128)));
limbSlotIcons.Add(InvSlotType.OuterClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.Bag, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
}
return limbSlotIcons;
}
@@ -922,12 +923,14 @@ namespace Barotrauma
if (slotItem == item)
{
slot.ShowBorderHighlight(GUI.Style.Red, 0.1f, 0.4f);
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
break;
}
}
}
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity> { item }, true));
item.Remove();
return;
}
@@ -1065,7 +1068,7 @@ namespace Barotrauma
}
draggingItem = null;
GUI.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail);
SoundPlayer.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail);
}
public void DrawOwn(SpriteBatch spriteBatch)
@@ -261,7 +261,23 @@ namespace Barotrauma.Items.Components
if (!isNetworkMessage || open != PredictedState)
{
StopPicking(null);
PlaySound(forcedOpen ? ActionType.OnPicked : ActionType.OnUse);
ActionType actionType = ActionType.OnUse;
if (forcedOpen)
{
actionType = ActionType.OnPicked;
}
else
{
if (open && HasSoundsOfType[(int)ActionType.OnOpen])
{
actionType = ActionType.OnOpen;
}
else if (!open && HasSoundsOfType[(int)ActionType.OnClose])
{
actionType = ActionType.OnClose;
}
}
PlaySound(actionType);
if (isOpen) { stuck = MathHelper.Clamp(stuck - StuckReductionOnOpen, 0.0f, 100.0f); }
}
}
@@ -263,7 +263,7 @@ namespace Barotrauma.Items.Components
{
for (int i = 0; i < targetSections.Count; i++)
{
targetHull.SetSectionColorOrStrength(targetSections[i], color, sizeAdjustedSprayStrength * deltaTime, true, false);
targetHull.IncreaseSectionColorOrStrength(targetSections[i], color, sizeAdjustedSprayStrength * deltaTime, true, false);
}
}
else
@@ -52,6 +52,8 @@ namespace Barotrauma.Items.Components
get { return sounds.Count > 0; }
}
public bool[] HasSoundsOfType { get { return hasSoundsOfType; } }
private readonly bool[] hasSoundsOfType;
private readonly Dictionary<ActionType, List<ItemSound>> sounds;
private Dictionary<ActionType, SoundSelectionMode> soundSelectionModes;
@@ -191,6 +193,12 @@ namespace Barotrauma.Items.Components
private ItemSound loopingSound;
private SoundChannel loopingSoundChannel;
private List<SoundChannel> playingOneshotSoundChannels = new List<SoundChannel>();
public ItemComponent ReplacedBy;
public ItemComponent GetReplacementOrThis()
{
return ReplacedBy?.GetReplacementOrThis() ?? this;
}
public void UpdateSounds()
{
@@ -26,29 +26,17 @@ namespace Barotrauma.Items.Components
if (isHUDsHidden == value) { return; }
if (value == true)
{
ToggleCrewArea(false, storeOriginalState: true);
GameMain.GameSession?.CrewManager?.AutoHideCrewList();
ToggleChatBox(false, storeOriginalState: true);
}
else
{
ToggleCrewArea(crewAreaOriginalState, storeOriginalState: false);
GameMain.GameSession?.CrewManager?.ResetCrewList();
ToggleChatBox(chatBoxOriginalState, storeOriginalState: false);
}
isHUDsHidden = value;
}
private void ToggleCrewArea(bool value, bool storeOriginalState)
{
var crewManager = GameMain.GameSession?.CrewManager;
if (crewManager == null) { return; }
if (storeOriginalState)
{
crewAreaOriginalState = crewManager.ToggleCrewListOpen;
}
crewManager.ToggleCrewListOpen = value;
}
private void ToggleChatBox(bool value, bool storeOriginalState)
{
var crewManager = GameMain.GameSession?.CrewManager;
@@ -17,7 +17,16 @@ namespace Barotrauma.Items.Components
private GUITickBox autoControlIndicator;
private List<Pair<Vector2, ParticleEmitter>> pumpOutEmitters = new List<Pair<Vector2, ParticleEmitter>>();
private List<Pair<Vector2, ParticleEmitter>> pumpInEmitters = new List<Pair<Vector2, ParticleEmitter>>();
private List<Pair<Vector2, ParticleEmitter>> pumpInEmitters = new List<Pair<Vector2, ParticleEmitter>>();
public float CurrentBrokenVolume
{
get
{
if (item.ConditionPercentage > 10.0f || !IsActive) { return 0.0f; }
return (1.0f - item.ConditionPercentage / 10.0f) * 100.0f;
}
}
partial void InitProjSpecific(XElement element)
{
@@ -284,6 +284,7 @@ namespace Barotrauma.Items.Components
{
Enabled = false,
Selected = AutoTemp,
ClickSound = GUISoundType.UISwitch,
OnClicked = (button, data) =>
{
AutoTemp = !AutoTemp;
@@ -181,6 +181,7 @@ namespace Barotrauma.Items.Components
{
Selected = false,
Enabled = true,
ClickSound = GUISoundType.UISwitch,
OnClicked = (button, data) =>
{
button.Selected = !button.Selected;
@@ -123,6 +123,7 @@ namespace Barotrauma.Items.Components
{
Selected = autoPilot,
Enabled = true,
ClickSound = GUISoundType.UISwitch,
OnClicked = (button, data) =>
{
button.Selected = !button.Selected;
@@ -535,6 +535,9 @@ namespace Barotrauma.Items.Components
nodes = nodePositions.ToList();
UpdateSections();
Drawable = nodes.Any();
IsActive =
(connections[0] == null ^ connections[1] == null) &&
(item.ParentInventory is CharacterInventory characterInventory && ((characterInventory.Owner as Character)?.HasEquippedItem(item) ?? false));
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
@@ -35,6 +35,7 @@ namespace Barotrauma.Items.Components
private RoundSound startMoveSound, endMoveSound, moveSound;
private SoundChannel moveSoundChannel;
private Vector2 oldRotation = Vector2.Zero;
private Vector2 crosshairPos, crosshairPointerPos;
@@ -307,7 +308,11 @@ namespace Barotrauma.Items.Components
if (!item.IsSelected) { return; }
Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: 10, initMethod: (widget) =>
{
{
widget.Selected += () =>
{
oldRotation = RotationLimits;
};
widget.MouseDown += () =>
{
widget.color = GUI.Style.Green;
@@ -317,6 +322,10 @@ namespace Barotrauma.Items.Components
{
widget.color = Color.Yellow;
item.CreateEditingHUD();
if (SubEditorScreen.IsSubEditor())
{
SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation));
}
};
widget.MouseHeld += (deltaTime) =>
{
@@ -345,10 +354,14 @@ namespace Barotrauma.Items.Components
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * widgetRadius;
widget.Update(deltaTime);
};
});
});
Widget maxRotationWidget = GetWidget("maxrotation", spriteBatch, size: 10, initMethod: (widget) =>
{
widget.Selected += () =>
{
oldRotation = RotationLimits;
};
widget.MouseDown += () =>
{
widget.color = GUI.Style.Green;
@@ -358,6 +371,10 @@ namespace Barotrauma.Items.Components
{
widget.color = Color.Yellow;
item.CreateEditingHUD();
if (SubEditorScreen.IsSubEditor())
{
SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation));
}
};
widget.MouseHeld += (deltaTime) =>
{
@@ -3,7 +3,6 @@ using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -163,6 +162,8 @@ namespace Barotrauma
public static Inventory DraggingInventory;
public Inventory ReplacedBy;
public Rectangle BackgroundFrame { get; protected set; }
private ushort[] receivedItemIDs;
@@ -317,6 +318,11 @@ namespace Barotrauma
}
}
public Inventory GetReplacementOrThiS()
{
return ReplacedBy?.GetReplacementOrThiS() ?? this;
}
public virtual void CreateSlots()
{
slots = new InventorySlot[capacity];
@@ -465,12 +471,15 @@ namespace Barotrauma
{
if (item != null)
{
if (mouseDrag) { item.OwnInventory?.DeleteAllItems(); }
slot.ShowBorderHighlight(GUI.Style.Red, 0.1f, 0.4f);
if (!mouseDrag)
{
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
}
SubEditorScreen.BulkItemBufferInUse = true;
SubEditorScreen.BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { item }, true));
item.OwnInventory?.DeleteAllItems();
item.Remove();
}
}
@@ -996,10 +1005,19 @@ namespace Barotrauma
Character.Controlled.FocusedItem.OwnInventory.CanBePut(draggingItem) &&
Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled))
{
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
}
else
{
if (Screen.Selected is SubEditorScreen)
{
if (draggingItem?.ParentInventory != null)
{
SubEditorScreen.StoreCommand(new InventoryPlaceCommand(draggingItem.ParentInventory, new List<Item> { draggingItem }, true));
}
}
SoundPlayer.PlayUISound(GUISoundType.DropItem);
bool removed = false;
if (Screen.Selected is SubEditorScreen editor)
{
@@ -1025,14 +1043,16 @@ namespace Barotrauma
{
draggingItem.Drop(Character.Controlled);
}
GUI.PlayUISound(removed ? GUISoundType.PickItem : GUISoundType.DropItem);
SoundPlayer.PlayUISound(removed ? GUISoundType.PickItem : GUISoundType.DropItem);
}
}
else if (selectedSlot.ParentInventory.Items[selectedSlot.SlotIndex] != draggingItem)
{
Inventory oldInventory = draggingItem.ParentInventory;
Inventory selectedInventory = selectedSlot.ParentInventory;
int slotIndex = selectedSlot.SlotIndex;
int oldSlot = oldInventory == null ? 0 : Array.IndexOf(oldInventory.Items, draggingItem);
//if attempting to drop into an invalid slot in the same inventory, try to move to the correct slot
if (selectedInventory.Items[slotIndex] == null &&
@@ -1051,17 +1071,21 @@ namespace Barotrauma
}
selectedInventory.slots[slotIndex].ShowBorderHighlight(GUI.Style.Red, 0.1f, 0.9f);
}
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
}
else if (selectedInventory.TryPutItem(draggingItem, slotIndex, true, true, Character.Controlled))
{
if (SubEditorScreen.IsSubEditor())
{
SubEditorScreen.StoreCommand(new InventoryMoveCommand(oldInventory, selectedInventory, draggingItem, oldSlot, slotIndex));
}
if (selectedInventory.slots != null) { selectedInventory.slots[slotIndex].ShowBorderHighlight(Color.White, 0.1f, 0.4f); }
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
}
else
{
if (selectedInventory.slots != null){ selectedInventory.slots[slotIndex].ShowBorderHighlight(GUI.Style.Red, 0.1f, 0.9f); }
GUI.PlayUISound(GUISoundType.PickItemFail);
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
}
selectedInventory.HideTimer = 2.0f;
if (selectedSlot.ParentInventory?.Owner is Item parentItem && parentItem.ParentInventory != null)
@@ -542,7 +542,7 @@ namespace Barotrauma
Spacing = (int)(25 * GUI.Scale)
};
var itemEditor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUI.LargeFont);
var itemEditor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUI.LargeFont) { UserData = this };
itemEditor.Children.First().Color = Color.Black * 0.7f;
if (!inGame)
{
@@ -663,7 +663,7 @@ namespace Barotrauma
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), listBox.Content.RectTransform), style: "HorizontalLine");
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame, showName: !inGame, titleFont: GUI.SubHeadingFont);
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame, showName: !inGame, titleFont: GUI.SubHeadingFont) { UserData = ic };
componentEditor.Children.First().Color = Color.Black * 0.7f;
if (inGame)
@@ -97,10 +97,12 @@ namespace Barotrauma
{
if (potentialContainer?.OwnInventory?.TryPutItem(item, Character.Controlled) ?? false)
{
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
}
}
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity> {item}, false));
placePosition = Vector2.Zero;
return;
}
@@ -110,5 +110,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
static partial void PlayTinnitusProjSpecific(float volume) => SoundPlayer.PlaySound("tinnitus", volume: volume);
}
}
@@ -11,11 +11,18 @@ namespace Barotrauma
partial void UpdateProjSpecific(float growModifier)
{
EmitParticles(size, WorldPosition, hull, growModifier, OnChangeHull);
if (this is DummyFireSource)
{
EmitParticles(size, WorldPosition, hull, growModifier, null);
}
else
{
EmitParticles(size, WorldPosition, hull, growModifier, OnChangeHull);
}
lightSource.Color = new Color(1.0f, 0.45f, 0.3f) * Rand.Range(0.8f, 1.0f);
if (Math.Abs((lightSource.Range * 0.2f) - Math.Max(size.X, size.Y)) > 1.0f) lightSource.Range = Math.Max(size.X, size.Y) * 5.0f;
if (Vector2.DistanceSquared(lightSource.Position, position) > 5.0f) lightSource.Position = position + Vector2.UnitY * 30.0f;
if (Math.Abs((lightSource.Range * 0.2f) - Math.Max(size.X, size.Y)) > 1.0f) { lightSource.Range = Math.Max(size.X, size.Y) * 5.0f; }
if (Vector2.DistanceSquared(lightSource.Position, position) > 5.0f) { lightSource.Position = position + Vector2.UnitY * 30.0f; }
}
public static void EmitParticles(Vector2 size, Vector2 worldPosition, Hull hull, float growModifier, Particle.OnChangeHullHandler onChangeHull = null)
@@ -32,6 +39,8 @@ namespace Barotrauma
(particlePos.X - (worldPosition.X + size.X / 2.0f)),
(float)Math.Sqrt(size.X) * Rand.Range(0.0f, 15.0f) * growModifier);
particleVel.X = MathHelper.Clamp(particleVel.X, -200.0f, 200.0f);
var particle = GameMain.ParticleManager.CreateParticle("flame",
particlePos, particleVel, 0.0f, hull);
@@ -261,6 +261,13 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, new Rectangle(fireSourceRect.X - (int)fs.DamageRange, fireSourceRect.Y, fireSourceRect.Width + (int)fs.DamageRange * 2, fireSourceRect.Height), GUI.Style.Orange, false, 0, 5);
//GUI.DrawRectangle(spriteBatch, new Rectangle((int)fs.LastExtinguishPos.X, (int)-fs.LastExtinguishPos.Y, 5,5), Color.Yellow, true);
}
foreach (FireSource fs in FakeFireSources)
{
Rectangle fireSourceRect = new Rectangle((int)fs.WorldPosition.X, -(int)fs.WorldPosition.Y, (int)fs.Size.X, (int)fs.Size.Y);
GUI.DrawRectangle(spriteBatch, fireSourceRect, GUI.Style.Red, false, 0, 5);
GUI.DrawRectangle(spriteBatch, new Rectangle(fireSourceRect.X - (int)fs.DamageRange, fireSourceRect.Y, fireSourceRect.Width + (int)fs.DamageRange * 2, fireSourceRect.Height), GUI.Style.Orange, false, 0, 5);
//GUI.DrawRectangle(spriteBatch, new Rectangle((int)fs.LastExtinguishPos.X, (int)-fs.LastExtinguishPos.Y, 5,5), Color.Yellow, true);
}
/*GUI.DrawLine(spriteBatch, new Vector2(drawRect.X, -WorldSurface), new Vector2(drawRect.Right, -WorldSurface), Color.Cyan * 0.5f);
@@ -623,12 +630,18 @@ namespace Barotrauma
for (int i = 0; i < decalCount; i++)
{
UInt32 decalId = message.ReadUInt32();
int spriteIndex = message.ReadByte();
float normalizedXPos = message.ReadRangedSingle(0.0f, 1.0f, 8);
float normalizedYPos = message.ReadRangedSingle(0.0f, 1.0f, 8);
float decalPosX = MathHelper.Lerp(rect.X, rect.Right, normalizedXPos);
float decalPosY = MathHelper.Lerp(rect.Y - rect.Height, rect.Y, normalizedYPos);
float decalScale = message.ReadRangedSingle(0.0f, 2.0f, 12);
AddDecal(decalId, new Vector2(decalPosX, decalPosY), decalScale, isNetworkEvent: true);
if (Submarine != null)
{
decalPosX += Submarine.Position.X;
decalPosY += Submarine.Position.Y;
}
AddDecal(decalId, new Vector2(decalPosX, decalPosY), decalScale, isNetworkEvent: true, spriteIndex: spriteIndex);
}
}
}
@@ -219,7 +219,7 @@ namespace Barotrauma.Lights
{
if (!light.IsBackground) { continue; }
light.DrawSprite(spriteBatch, cam);
if (light.Color.A > 0 && light.Range > 0.0f) { light.DrawLightVolume(spriteBatch, lightEffect, transform); }
light.DrawLightVolume(spriteBatch, lightEffect, transform);
}
GameMain.ParticleManager.Draw(spriteBatch, true, null, Particles.ParticleBlendState.Additive);
spriteBatch.End();
@@ -328,7 +328,7 @@ namespace Barotrauma.Lights
foreach (LightSource light in activeLights)
{
if (light.IsBackground) { continue; }
if (light.Color.A > 0 && light.Range > 0.0f) { light.DrawLightVolume(spriteBatch, lightEffect, transform); }
light.DrawLightVolume(spriteBatch, lightEffect, transform);
}
lightEffect.World = transform;
@@ -493,7 +493,7 @@ namespace Barotrauma.Lights
{
return null;
}
if (Range < 1.0f || Color.A < 0.01f) return null;
if (Range < 1.0f || Color.A < 1) { return null; }
Vector2 drawPos = position;
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
@@ -1134,6 +1134,8 @@ namespace Barotrauma.Lights
public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform)
{
if (Range < 1.0f || Color.A < 1) { return; }
if (CastShadows)
{
CheckHullsInRange();
@@ -1158,7 +1160,6 @@ namespace Barotrauma.Lights
return;
}
if (NeedsRecalculation)
{
var verts = FindRaycastHits();
@@ -1168,7 +1169,6 @@ namespace Barotrauma.Lights
NeedsRecalculation = false;
}
Vector2 offset = ParentSub == null ? Vector2.Zero : ParentSub.DrawPosition;
lightEffect.World =
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
@@ -27,6 +27,9 @@ namespace Barotrauma
private static bool resizing;
private int resizeDirX, resizeDirY;
private Rectangle? prevRect;
public static bool SelectionChanged;
//which entities have been selected for editing
private static List<MapEntity> selectedList = new List<MapEntity>();
@@ -105,6 +108,11 @@ namespace Barotrauma
return true;
}
/// <summary>
/// Used for undo/redo to determine what this item has been replaced with
/// </summary>
public MapEntity ReplacedBy;
public virtual void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { }
/// <summary>
@@ -147,13 +155,13 @@ namespace Barotrauma
}
if (GUI.KeyboardDispatcher.Subscriber == null)
{
if (PlayerInput.KeyDown(Keys.Delete))
if (PlayerInput.KeyHit(Keys.Delete))
{
selectedList.ForEach(e =>
if (selectedList.Any())
{
//orphaned wires may already have been removed
if (!e.Removed) { e.Remove(); }
});
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(selectedList, true));
}
selectedList.ForEach(e => { if (!e.Removed) { e.Remove(); } });
selectedList.Clear();
}
@@ -217,30 +225,6 @@ namespace Barotrauma
}
}
}
else if (PlayerInput.KeyHit(Keys.Z))
{
SetPreviousRects(e => e.rectMemento.Undo());
}
else if (PlayerInput.KeyHit(Keys.R))
{
SetPreviousRects(e => e.rectMemento.Redo());
}
void SetPreviousRects(Func<MapEntity, Rectangle> memoryMethod)
{
foreach (var e in SelectedList)
{
if (e.rectMemento != null)
{
Point diff = memoryMethod(e).Location - e.Rect.Location;
// We have to call the move method, because there's a lot more than just storing the rect (in some cases)
// We also have to reassign the rect, because the move method does not set the width and height. They might have changed too.
// The Rect property is virtual and it's overridden for structs. Setting the rect via the property should automatically recreate the sections for resizable structures.
e.Move(diff.ToVector2());
e.Rect = e.rectMemento.Current;
}
}
}
}
}
@@ -343,35 +327,38 @@ namespace Barotrauma
//clone
if (PlayerInput.IsCtrlDown())
{
var clones = Clone(selectedList);
var clones = Clone(selectedList).Where(c => c != null).ToList();
selectedList = clones;
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
selectedList.ForEach(c => c.Move(moveAmount));
}
else // move
{
var oldRects = selectedList.Select(e => e.Rect).ToList();
List<MapEntity> deposited = new List<MapEntity>();
foreach (MapEntity e in selectedList)
{
if (e.rectMemento == null)
{
e.rectMemento = new Memento<Rectangle>();
e.rectMemento.Store(e.Rect);
}
e.Move(moveAmount);
if (isShiftDown && e is Item item && targetContainer != null)
{
if (targetContainer.OwnInventory.TryPutItem(item, Character.Controlled))
{
GUI.PlayUISound(GUISoundType.DropItem);
SoundPlayer.PlayUISound(GUISoundType.DropItem);
deposited.Add(item);
}
else
{
GUI.PlayUISound(GUISoundType.PickItemFail);
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
}
}
e.rectMemento.Store(e.Rect);
}
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity>(selectedList),selectedList.Select(entity => entity.Rect).ToList(), oldRects, false));
if (deposited.Any() && deposited.Any(entity => entity is Item))
{
var depositedItems = deposited.Where(entity => entity is Item).Cast<Item>().ToList();
SubEditorScreen.StoreCommand(new InventoryPlaceCommand(targetContainer.OwnInventory, depositedItems, false));
}
deposited.ForEach(entity => { selectedList.Remove(entity); });
@@ -492,6 +479,11 @@ namespace Barotrauma
}
}
public MapEntity GetReplacementOrThis()
{
return ReplacedBy?.GetReplacementOrThis() ?? this;
}
public static Item GetPotentialContainer(Vector2 position, List<MapEntity> entities = null)
{
Item targetContainer = null;
@@ -904,13 +896,12 @@ namespace Barotrauma
public static void Cut(List<MapEntity> entities)
{
if (entities.Count == 0) { return; }
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(entities), true));
CopyEntities(entities);
entities.ForEach(e =>
{
e.Remove();
});
entities.ForEach(e => { if (!e.Removed) { e.Remove(); } });
entities.Clear();
}
@@ -922,6 +913,7 @@ namespace Barotrauma
Clone(copiedList);
var clones = mapEntityList.Except(prevEntities).ToList();
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
var nonWireClones = clones.Where(c => !(c is Item item) || item.GetComponent<Wire>() == null);
if (!nonWireClones.Any()) { nonWireClones = clones; }
@@ -1031,12 +1023,11 @@ namespace Barotrauma
if (resizing)
{
if (rectMemento == null)
if (prevRect == null)
{
rectMemento = new Memento<Rectangle>();
rectMemento.Store(Rect);
prevRect = new Rectangle(Rect.Location, Rect.Size);
}
Vector2 placePosition = new Vector2(rect.X, rect.Y);
Vector2 placeSize = new Vector2(rect.Width, rect.Height);
@@ -1079,9 +1070,15 @@ namespace Barotrauma
if (!PlayerInput.PrimaryMouseButtonHeld())
{
rectMemento.Store(Rect);
resizing = false;
Resized?.Invoke(rect);
if (prevRect != null)
{
var newData = new List<Rectangle> { Rect };
var oldData = new List<Rectangle> { prevRect.Value };
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity> { this }, newData, oldData, true));
}
prevRect = null;
}
}
}
@@ -93,7 +93,7 @@ namespace Barotrauma
int heightScaled = (int)(20 * GUI.Scale);
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this };
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null);
var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUI.LargeFont);
var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUI.LargeFont) { UserData = this };
if (Submarine.MainSub?.Info?.Type == SubmarineType.OutpostModule)
{
@@ -56,11 +56,12 @@ namespace Barotrauma
if (PlayerInput.PrimaryMouseButtonReleased())
{
newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position);
new Structure(newRect, this, Submarine.MainSub)
var structure = new Structure(newRect, this, Submarine.MainSub)
{
Submarine = Submarine.MainSub
};
};
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity> { structure }, false));
selected = null;
return;
}
@@ -95,7 +95,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);
GameAnalyticsManager.AddErrorEventOnce("Submarine.LoadRoundSound:FilePathEmpty" + element.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
return null;
}
@@ -107,7 +107,7 @@ namespace Barotrauma
}
else
{
existingSound = roundSounds.Find(s => s.Filename == filename && s.Stream == stream)?.Sound;
existingSound = roundSounds.Find(s => s.Filename == filename && s.Stream == stream && !s.Sound.Disposed)?.Sound;
}
if (existingSound == null)
@@ -121,7 +121,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);
GameAnalyticsManager.AddErrorEventOnce("Submarine.LoadRoundSound:FileNotFound" + filename, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
return null;
}
}
@@ -132,6 +132,26 @@ namespace Barotrauma
return newSound;
}
public static void ReloadRoundSound(RoundSound roundSound)
{
Sound existingSound = roundSounds?.Find(s => s.Filename == roundSound.Filename && s.Stream == roundSound.Stream && !s.Sound.Disposed)?.Sound;
if (existingSound == null)
{
try
{
existingSound = GameMain.SoundManager.LoadSound(roundSound.Filename, roundSound.Stream);
}
catch (System.IO.FileNotFoundException e)
{
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());
return;
}
}
roundSound.Sound = existingSound;
}
private static void RemoveRoundSound(RoundSound roundSound)
{
roundSound.Sound?.Dispose();
@@ -438,10 +458,15 @@ namespace Barotrauma
public void CheckForErrors()
{
List<string> errorMsgs = new List<string>();
List<SubEditorScreen.WarningType> warnings = new List<SubEditorScreen.WarningType>();
if (!Hull.hullList.Any())
{
errorMsgs.Add(TextManager.Get("NoHullsWarning"));
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoWaypoints))
{
errorMsgs.Add(TextManager.Get("NoHullsWarning"));
warnings.Add(SubEditorScreen.WarningType.NoHulls);
}
}
if (Info.Type != SubmarineType.OutpostModule ||
@@ -449,7 +474,11 @@ namespace Barotrauma
{
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Path))
{
errorMsgs.Add(TextManager.Get("NoWaypointsWarning"));
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoWaypoints))
{
errorMsgs.Add(TextManager.Get("NoWaypointsWarning"));
warnings.Add(SubEditorScreen.WarningType.NoWaypoints);
}
}
}
@@ -460,22 +489,38 @@ namespace Barotrauma
if (item.GetComponent<Items.Components.Vent>() == null) { continue; }
if (!item.linkedTo.Any())
{
errorMsgs.Add(TextManager.Get("DisconnectedVentsWarning"));
if (!IsWarningSuppressed(SubEditorScreen.WarningType.DisconnectedVents))
{
errorMsgs.Add(TextManager.Get("DisconnectedVentsWarning"));
warnings.Add(SubEditorScreen.WarningType.DisconnectedVents);
}
break;
}
}
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Human))
{
errorMsgs.Add(TextManager.Get("NoHumanSpawnpointWarning"));
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoHumanSpawnpoints))
{
errorMsgs.Add(TextManager.Get("NoHumanSpawnpointWarning"));
warnings.Add(SubEditorScreen.WarningType.NoHumanSpawnpoints);
}
}
if (WayPoint.WayPointList.Find(wp => wp.SpawnType == SpawnType.Cargo) == null)
{
errorMsgs.Add(TextManager.Get("NoCargoSpawnpointWarning"));
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoCargoSpawnpoints))
{
errorMsgs.Add(TextManager.Get("NoCargoSpawnpointWarning"));
warnings.Add(SubEditorScreen.WarningType.NoCargoSpawnpoints);
}
}
if (!Item.ItemList.Any(it => it.GetComponent<Items.Components.Pump>() != null && it.HasTag("ballast")))
{
errorMsgs.Add(TextManager.Get("NoBallastTagsWarning"));
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoBallastTag))
{
errorMsgs.Add(TextManager.Get("NoBallastTagsWarning"));
warnings.Add(SubEditorScreen.WarningType.NoBallastTag);
}
}
}
else if (Info.Type == SubmarineType.OutpostModule)
@@ -503,7 +548,11 @@ namespace Barotrauma
if (Gap.GapList.Any(g => g.linkedTo.Count == 0))
{
errorMsgs.Add(TextManager.Get("NonLinkedGapsWarning"));
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NonLinkedGaps))
{
errorMsgs.Add(TextManager.Get("NonLinkedGapsWarning"));
warnings.Add(SubEditorScreen.WarningType.NonLinkedGaps);
}
}
int disabledItemLightCount = 0;
@@ -515,12 +564,35 @@ namespace Barotrauma
int count = GameMain.LightManager.Lights.Count(l => l.CastShadows) - disabledItemLightCount;
if (count > 45)
{
errorMsgs.Add(TextManager.Get("subeditor.shadowcastinglightswarning"));
if (!IsWarningSuppressed(SubEditorScreen.WarningType.TooManyLights))
{
errorMsgs.Add(TextManager.Get("subeditor.shadowcastinglightswarning"));
warnings.Add(SubEditorScreen.WarningType.TooManyLights);
}
}
if (errorMsgs.Any())
{
new GUIMessageBox(TextManager.Get("Warning"), string.Join("\n\n", errorMsgs), new Vector2(0.25f, 0.0f), new Point(400, 200));
GUIMessageBox msgBox = new GUIMessageBox(TextManager.Get("Warning"), string.Join("\n\n", errorMsgs), new Vector2(0.25f, 0.0f), new Point(400, 200));
if (warnings.Any())
{
Point size = msgBox.RectTransform.NonScaledSize;
GUITickBox suppress = new GUITickBox(new RectTransform(new Vector2(1f, 0.33f), msgBox.Content.RectTransform), TextManager.Get("editor.suppresswarnings"));
msgBox.RectTransform.NonScaledSize = new Point(size.X, size.Y + suppress.RectTransform.NonScaledSize.Y);
msgBox.Buttons[0].OnClicked += (button, obj) =>
{
if (suppress.Selected)
{
foreach (SubEditorScreen.WarningType warning in warnings.Where(warning => !SubEditorScreen.SuppressedWarnings.Contains(warning)))
{
SubEditorScreen.SuppressedWarnings.Add(warning);
}
}
return true;
};
}
}
foreach (MapEntity e in MapEntity.mapEntityList)
@@ -556,6 +628,11 @@ namespace Barotrauma
}
}
bool IsWarningSuppressed(SubEditorScreen.WarningType type)
{
return SubEditorScreen.SuppressedWarnings.Contains(type);
}
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
@@ -1,5 +1,5 @@
using System;
using System.Linq;
using Microsoft.Xna.Framework;
using System;
namespace Barotrauma.Networking
{
@@ -49,45 +49,53 @@ namespace Barotrauma.Networking
Character targetCharacter = Entity.FindEntityByID(targetCharacterID) as Character;
Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16());
int optionIndex = msg.ReadByte();
OrderTarget orderTargetPosition = null;
if (msg.ReadBoolean())
{
var x = msg.ReadSingle();
var y = msg.ReadSingle();
var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull;
orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true);
}
Order order = null;
Order orderPrefab;
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
{
DebugConsole.ThrowError("Invalid order message - order index out of bounds.");
if (NetIdUtils.IdMoreRecent(ID, LastID)) LastID = ID;
if (NetIdUtils.IdMoreRecent(ID, LastID)) { LastID = ID; }
return;
}
else
{
order = Order.PrefabList[orderIndex];
orderPrefab = Order.PrefabList[orderIndex];
}
string orderOption = "";
if (optionIndex >= 0 && optionIndex < order.Options.Length)
if (optionIndex >= 0 && optionIndex < orderPrefab.Options.Length)
{
orderOption = order.Options[optionIndex];
orderOption = orderPrefab.Options[optionIndex];
}
txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);
txt = orderPrefab.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{
var order = orderTargetPosition == null ?
new Order(orderPrefab, targetEntity, orderPrefab.GetTargetItemComponent(targetEntity as Item), orderGiver: senderCharacter) :
new Order(orderPrefab, orderTargetPosition, orderGiver: senderCharacter);
if (order.TargetAllCharacters)
{
GameMain.GameSession?.CrewManager?.AddOrder(
new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
order.Prefab.FadeOutTime);
GameMain.GameSession?.CrewManager?.AddOrder(order, orderPrefab.FadeOutTime);
}
else if (targetCharacter != null)
{
targetCharacter.SetOrder(
new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
orderOption, senderCharacter);
targetCharacter.SetOrder(order, orderOption, senderCharacter);
}
}
if (NetIdUtils.IdMoreRecent(ID, LastID))
{
GameMain.Client.AddChatMessage(
new OrderChatMessage(order, orderOption, txt, targetEntity, targetCharacter, senderCharacter));
new OrderChatMessage(orderPrefab, orderOption, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter));
LastID = ID;
}
return;
@@ -608,10 +608,10 @@ namespace Barotrauma.Networking
{
string errorMsg = "Error while reading a message from server. {" + e + "}. ";
if (GameMain.Client == null) { errorMsg += "Client disposed."; }
errorMsg += "\n" + e.StackTrace;
errorMsg += "\n" + e.StackTrace.CleanupStackTrace();
if (e.InnerException != null)
{
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace;
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace.CleanupStackTrace();
}
GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + e.TargetSite.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
DebugConsole.ThrowError("Error while reading a message from server.", e);
@@ -738,10 +738,10 @@ namespace Barotrauma.Networking
}
catch (Exception e)
{
string errorMsg = "Error while reading an ingame update message from server. {" + e + "}\n" + e.StackTrace;
string errorMsg = "Error while reading an ingame update message from server. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
if (e.InnerException != null)
{
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace;
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace.CleanupStackTrace();
}
#if DEBUG
DebugConsole.ThrowError("Error while reading an ingame update message from server.", e);
@@ -755,7 +755,7 @@ namespace Barotrauma.Networking
{
string errorMsg = "Failed to read a voice packet from the server (VoipClient == null). ";
if (GameMain.Client == null) { errorMsg += "Client disposed. "; }
errorMsg += "\n" + Environment.StackTrace;
errorMsg += "\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce(
"GameClient.ReadDataMessage:VoipClientNull",
GameMain.Client == null ? GameAnalyticsSDK.Net.EGAErrorSeverity.Error : GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
@@ -1124,7 +1124,7 @@ namespace Barotrauma.Networking
else
{
DebugConsole.NewMessage("Not attempting to reconnect (DisconnectReason doesn't allow reconnection).");
msg = TextManager.Get("DisconnectReason." + disconnectReason.ToString());
msg = TextManager.Get("DisconnectReason." + disconnectReason.ToString()) + " ";
for (int i = 1; i < splitMsg.Length; i++)
{
@@ -1966,7 +1966,12 @@ namespace Barotrauma.Networking
string selectShuttleName = inc.ReadString();
string selectShuttleHash = inc.ReadString();
string campaignSubmarineIndexes = inc.ReadString();
UInt16 campaignSubmarineIndexCount = inc.ReadUInt16();
List<int> campaignSubIndices = new List<int>();
for (int i = 0; i< campaignSubmarineIndexCount; i++)
{
campaignSubIndices.Add(inc.ReadUInt16());
}
bool allowSubVoting = inc.ReadBoolean();
bool allowModeVoting = inc.ReadBoolean();
@@ -2022,22 +2027,16 @@ namespace Barotrauma.Networking
if (GameMain.Client.IsServerOwner) RequestSelectMode(modeIndex);
}
if (campaignSubmarineIndexes != null)
if (campaignSubIndices != null)
{
string[] activeIndexes = campaignSubmarineIndexes.Split(';');
GameMain.NetLobbyScreen.CampaignSubmarines = new List<SubmarineInfo>();
for (int i = 0; i < activeIndexes.Length; i++)
foreach (UInt16 campaignSubIndex in campaignSubIndices)
{
int index;
if (int.TryParse(activeIndexes[i], out index))
SubmarineInfo sub = GameMain.Client.ServerSubmarines[campaignSubIndex];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "campaign"))
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[index];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "campaign"))
{
GameMain.NetLobbyScreen.CampaignSubmarines.Add(sub);
}
}
GameMain.NetLobbyScreen.CampaignSubmarines.Add(sub);
}
}
if (HasPermission(ClientPermissions.ManageCampaign) && !gameStarted && GameMain.NetLobbyScreen?.CampaignSetupUI != null)
@@ -2186,7 +2185,7 @@ namespace Barotrauma.Networking
"Previous object was " + (prevBitLength) + " bits long (" + (prevByteLength) + " bytes)",
" "
};
errorLines.Add(ex.StackTrace);
errorLines.Add(ex.StackTrace.CleanupStackTrace());
errorLines.Add(" ");
if (prevObjHeader == ServerNetObject.ENTITY_EVENT || prevObjHeader == ServerNetObject.ENTITY_EVENT_INITIAL ||
objHeader == ServerNetObject.ENTITY_EVENT || objHeader == ServerNetObject.ENTITY_EVENT_INITIAL)
@@ -2721,7 +2720,7 @@ namespace Barotrauma.Networking
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
if (campaign == null)
{
DebugConsole.ThrowError("Failed send campaign state to the server (no campaign active).\n" + Environment.StackTrace);
DebugConsole.ThrowError("Failed send campaign state to the server (no campaign active).\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
@@ -2738,7 +2737,7 @@ namespace Barotrauma.Networking
{
if (string.IsNullOrWhiteSpace(command))
{
DebugConsole.ThrowError("Cannot send an empty console command to the server!\n" + Environment.StackTrace);
DebugConsole.ThrowError("Cannot send an empty console command to the server!\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
@@ -2778,7 +2777,7 @@ namespace Barotrauma.Networking
if (subIndex < 0 || subIndex >= subList.Content.CountChildren)
{
DebugConsole.ThrowError("Submarine index out of bounds (" + subIndex + ")\n" + Environment.StackTrace);
DebugConsole.ThrowError("Submarine index out of bounds (" + subIndex + ")\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
@@ -2818,7 +2817,7 @@ namespace Barotrauma.Networking
if (!HasPermission(ClientPermissions.SelectMode)) return;
if (modeIndex < 0 || modeIndex >= GameMain.NetLobbyScreen.ModeList.Content.CountChildren)
{
DebugConsole.ThrowError("Gamemode index out of bounds (" + modeIndex + ")\n" + Environment.StackTrace);
DebugConsole.ThrowError("Gamemode index out of bounds (" + modeIndex + ")\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
@@ -53,12 +53,12 @@ namespace Barotrauma.Networking
if (((Entity)entity).Removed)
{
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the entity has been removed.\n" + Environment.StackTrace);
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the entity has been removed.\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
if (((Entity)entity).IdFreed)
{
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the ID of the entity has been freed.\n" + Environment.StackTrace);
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the ID of the entity has been freed.\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
@@ -254,7 +254,7 @@ namespace Barotrauma.Networking
catch (Exception e)
{
string errorMsg = "Failed to read event for entity \"" + entity.ToString() + "\" (" + e.Message + ")! (MidRoundSyncing: " + thisClient.MidRoundSyncing + ")\n" + e.StackTrace;
string errorMsg = "Failed to read event for entity \"" + entity.ToString() + "\" (" + e.Message + ")! (MidRoundSyncing: " + thisClient.MidRoundSyncing + ")\n" + e.StackTrace.CleanupStackTrace();
errorMsg += "\nPrevious entities:";
for (int j = entities.Count - 2; j >= 0; j--)
{
@@ -1,5 +1,4 @@
using Lidgren.Network;
using System;
using System;
namespace Barotrauma.Networking
{
@@ -11,10 +10,20 @@ namespace Barotrauma.Networking
msg.Write(NetStateID);
msg.Write((byte)ChatMessageType.Order);
msg.Write((byte)Order.PrefabList.IndexOf(Order.Prefab));
msg.Write(TargetCharacter == null ? (UInt16)0 : TargetCharacter.ID);
msg.Write(TargetEntity == null ? (UInt16)0 : TargetEntity.ID);
msg.Write(TargetEntity is Entity ? (TargetEntity as Entity).ID : (UInt16)0);
msg.Write((byte)Array.IndexOf(Order.Prefab.Options, OrderOption));
if (TargetEntity is OrderTarget orderTarget)
{
msg.Write(true);
msg.Write(orderTarget.Position.X);
msg.Write(orderTarget.Position.Y);
msg.Write(orderTarget.Hull == null ? (UInt16)0 : orderTarget.Hull.ID);
}
else
{
msg.Write(false);
}
}
}
}
@@ -162,6 +162,7 @@ namespace Barotrauma.Networking
disconnectMsg = $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}";
}
Close(disconnectMsg, disableReconnect: true);
OnDisconnectMessageReceived?.Invoke(DisconnectReason.MissingContentPackage + "/" + disconnectMsg);
}
else
{
@@ -478,11 +478,14 @@ namespace Barotrauma.Steam
}
if (rules.ContainsKey("gamestarted")) serverInfo.GameStarted = rules["gamestarted"] == "True";
if (rules.ContainsKey("gamemode"))
{
serverInfo.GameMode = rules["gamemode"];
}
if (rules.ContainsKey("playstyle") && Enum.TryParse(rules["playstyle"], out PlayStyle playStyle))
{
serverInfo.PlayStyle = playStyle;
}
if (serverInfo.ContentPackageNames.Count != serverInfo.ContentPackageHashes.Count ||
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopIds.Count)
@@ -1297,7 +1300,7 @@ namespace Barotrauma.Steam
}
catch (Exception e)
{
errorMsg = "Disabling the workshop item \"" + item?.Title + "\" failed. " + e.Message + "\n" + e.StackTrace;
errorMsg = "Disabling the workshop item \"" + item?.Title + "\" failed. " + e.Message + "\n" + e.StackTrace.CleanupStackTrace();
if (!noLog)
{
DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red);
@@ -1492,7 +1495,7 @@ 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);
"Failed to autoupdate workshop item \"" + item.Title + "\". " + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
});
}
}
@@ -23,7 +23,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)
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)
{
emitTimer += deltaTime * amountMultiplier;
burstEmitTimer -= deltaTime;
@@ -33,7 +33,7 @@ namespace Barotrauma.Particles
float emitInterval = 1.0f / Prefab.ParticlesPerSecond;
while (emitTimer > emitInterval)
{
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier);
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle);
emitTimer -= emitInterval;
}
}
@@ -43,11 +43,11 @@ namespace Barotrauma.Particles
burstEmitTimer = Prefab.EmitInterval;
for (int i = 0; i < Prefab.ParticleAmount * amountMultiplier; i++)
{
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier);
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle);
}
}
private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null)
private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null)
{
angle += Rand.Range(Prefab.AngleMin, Prefab.AngleMax);
@@ -55,13 +55,20 @@ namespace Barotrauma.Particles
Vector2 velocity = dir * Rand.Range(Prefab.VelocityMin, Prefab.VelocityMax) * velocityMultiplier;
position += dir * Rand.Range(Prefab.DistanceMin, Prefab.DistanceMax);
var particle = GameMain.ParticleManager.CreateParticle(Prefab.ParticlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop);
var particle = GameMain.ParticleManager.CreateParticle(overrideParticle ?? Prefab.ParticlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop);
if (particle != null)
{
particle.Size *= Rand.Range(Prefab.ScaleMin, Prefab.ScaleMax) * sizeMultiplier;
particle.HighQualityCollisionDetection = Prefab.HighQualityCollisionDetection;
if (colorMultiplier.HasValue) { particle.ColorMultiplier = colorMultiplier.Value.ToVector4(); }
if (colorMultiplier.HasValue)
{
particle.ColorMultiplier = colorMultiplier.Value.ToVector4();
}
else if (Prefab.ColorMultiplier != Color.White)
{
particle.ColorMultiplier = Prefab.ColorMultiplier.ToVector4();
}
}
}
@@ -138,6 +145,8 @@ namespace Barotrauma.Particles
public readonly bool CopyEntityAngle;
public readonly Color ColorMultiplier;
public bool DrawOnTop => forceDrawOnTop || ParticlePrefab.DrawOnTop;
private readonly bool forceDrawOnTop;
@@ -204,11 +213,12 @@ namespace Barotrauma.Particles
}
EmitInterval = element.GetAttributeFloat("emitinterval", 0.0f);
ParticlesPerSecond = element.GetAttributeInt("particlespersecond", 0);
ParticlesPerSecond = element.GetAttributeFloat("particlespersecond", 0);
ParticleAmount = element.GetAttributeInt("particleamount", 0);
HighQualityCollisionDetection = element.GetAttributeBool("highqualitycollisiondetection", false);
CopyEntityAngle = element.GetAttributeBool("copyentityangle", false);
forceDrawOnTop = element.GetAttributeBool("drawontop", false);
forceDrawOnTop = element.GetAttributeBool("drawontop", false);
ColorMultiplier = element.GetAttributeColor("colormultiplier", Color.White);
}
}
}
@@ -150,7 +150,7 @@ namespace Barotrauma
}
sb.AppendLine("\n");
sb.AppendLine("Game version " + GameMain.Version +
" (" + AssemblyInfo.GetBuildString() + ", branch " + AssemblyInfo.GetGitBranch() + ", revision " + AssemblyInfo.GetGitRevision() + ")");
" (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")");
if (GameMain.Config != null)
{
sb.AppendLine("Graphics mode: " + GameMain.Config.GraphicsWidth + "x" + GameMain.Config.GraphicsHeight + " (" + GameMain.Config.WindowMode.ToString() + ")");
@@ -219,7 +219,7 @@ namespace Barotrauma
}
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.StackTrace);
sb.AppendLine(exception.StackTrace.CleanupStackTrace());
sb.AppendLine("\n");
if (exception.InnerException != null)
@@ -230,7 +230,7 @@ namespace Barotrauma
sb.AppendLine("Target site: " + exception.InnerException.TargetSite.ToString());
}
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.InnerException.StackTrace);
sb.AppendLine(exception.InnerException.StackTrace.CleanupStackTrace());
}
sb.AppendLine("Last debug messages:");
@@ -306,7 +306,7 @@ namespace Barotrauma.CharacterEditor
var lastJoint = selectedJoints.LastOrDefault();
if (lastJoint != null)
{
lastLimb = PlayerInput.KeyDown(Keys.LeftAlt) ? lastJoint.LimbB : lastJoint.LimbA;
lastLimb = PlayerInput.KeyDown(Keys.LeftAlt) ? lastJoint.LimbA : lastJoint.LimbB;
}
}
if (lastLimb != null)
@@ -374,6 +374,14 @@ namespace Barotrauma.CharacterEditor
if (Wizard.instance != null) { return; }
spriteSheetRect = CalculateSpritesheetRectangle();
// Handle shortcut keys
if (PlayerInput.KeyHit(Keys.F1))
{
SetToggle(paramsToggle, !paramsToggle.Selected);
}
if (PlayerInput.KeyHit(Keys.F5))
{
RecreateRagdoll();
}
if (GUI.KeyboardDispatcher.Subscriber == null)
{
if (PlayerInput.KeyHit(Keys.D1))
@@ -445,10 +453,6 @@ namespace Barotrauma.CharacterEditor
{
SetToggle(showCollidersToggle, !showCollidersToggle.Selected);
}
if (PlayerInput.KeyHit(Keys.Tab))
{
SetToggle(paramsToggle, !paramsToggle.Selected);
}
if (PlayerInput.KeyHit(Keys.L))
{
SetToggle(lightsToggle, !lightsToggle.Selected);
@@ -469,10 +473,6 @@ namespace Barotrauma.CharacterEditor
{
SetToggle(ikToggle, !ikToggle.Selected);
}
if (PlayerInput.KeyHit(Keys.F5))
{
RecreateRagdoll();
}
}
if (PlayerInput.KeyDown(InputType.Left) || PlayerInput.KeyDown(InputType.Right) || PlayerInput.KeyDown(InputType.Up) || PlayerInput.KeyDown(InputType.Down))
{
@@ -809,7 +809,13 @@ namespace Barotrauma.CharacterEditor
else if (showColliders)
{
character.AnimController.Collider.DebugDraw(spriteBatch, Color.White, forceColor: true);
character.AnimController.Limbs.ForEach(l => l.body.DebugDraw(spriteBatch, GUI.Style.Green, forceColor: true));
foreach (var limb in character.AnimController.Limbs)
{
if (!limb.Hide)
{
limb.body.DebugDraw(spriteBatch, GUI.Style.Green, forceColor: true);
}
}
}
spriteBatch.End();
@@ -935,7 +941,7 @@ namespace Barotrauma.CharacterEditor
var lastJoint = selectedJoints.LastOrDefault();
if (lastJoint != null)
{
lastLimb = PlayerInput.KeyDown(Keys.LeftAlt) ? lastJoint.LimbB : lastJoint.LimbA;
lastLimb = PlayerInput.KeyDown(Keys.LeftAlt) ? lastJoint.LimbA : lastJoint.LimbB;
}
}
if (lastLimb != null)
@@ -953,14 +959,14 @@ namespace Barotrauma.CharacterEditor
{
UpdateOtherLimbs(lastLimb, l => TryUpdateSubParam(l.Params, "spriteorientation", angle));
}
}, circleRadius: 40, widgetSize: 15, rotationOffset: MathHelper.Pi, autoFreeze: false, rounding: 10);
}, circleRadius: 40, widgetSize: 15, rotationOffset: 0, autoFreeze: false, rounding: 10);
}
else
{
var topLeft = spriteSheetControls.RectTransform.TopLeft;
GUI.DrawString(spriteBatch, new Vector2(topLeft.X + 350 * GUI.xScale, GameMain.GraphicsHeight - 95 * GUI.yScale), GetCharacterEditorTranslation("SpriteSheetOrientation") + ":", Color.White, Color.Gray * 0.5f, 10, GUI.Font);
DrawRadialWidget(spriteBatch, new Vector2(topLeft.X + 610 * GUI.xScale, GameMain.GraphicsHeight - 75 * GUI.yScale), RagdollParams.SpritesheetOrientation, string.Empty, Color.White,
angle => TryUpdateRagdollParam("spritesheetorientation", angle), circleRadius: 40, widgetSize: 15, rotationOffset: MathHelper.Pi, autoFreeze: false, rounding: 10);
angle => TryUpdateRagdollParam("spritesheetorientation", angle), circleRadius: 40, widgetSize: 15, rotationOffset: 0, autoFreeze: false, rounding: 10);
}
}
// Debug
@@ -2383,7 +2389,7 @@ namespace Barotrauma.CharacterEditor
IEnumerable<Limb> limbs = selectedLimbs;
if (limbs.None())
{
limbs = selectedJoints.Select(j => PlayerInput.KeyDown(Keys.LeftAlt) ? j.LimbB : j.LimbA);
limbs = selectedJoints.Select(j => PlayerInput.KeyDown(Keys.LeftAlt) ? j.LimbA : j.LimbB);
}
foreach (var limb in limbs)
{
@@ -3499,12 +3505,8 @@ namespace Barotrauma.CharacterEditor
limbJoint.UpperLimit += MathHelper.TwoPi;
}
}
if (limbJoint.UpperLimit - limbJoint.LowerLimit > MathHelper.TwoPi)
{
limbJoint.LowerLimit = MathUtils.WrapAnglePi(limbJoint.LowerLimit);
limbJoint.UpperLimit = MathUtils.WrapAnglePi(limbJoint.UpperLimit);
}
limbJoint.LowerLimit = MathUtils.WrapAnglePi(limbJoint.LowerLimit);
limbJoint.UpperLimit = MathUtils.WrapAnglePi(limbJoint.UpperLimit);
}
private Limb GetClosestLimbOnRagdoll(Vector2 targetPos, Func<Limb, bool> filter = null)
@@ -4392,7 +4394,7 @@ namespace Barotrauma.CharacterEditor
if (!altDown && editJoints && selectedJoints.Any() && jointCreationMode == JointCreationMode.None)
{
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 180, 250), GetCharacterEditorTranslation("HoldLeftAltToManipulateJoint"), Color.White, Color.Black * 0.5f, 10, GUI.Font);
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 180, 100), GetCharacterEditorTranslation("HoldLeftAltToManipulateJoint"), Color.White, Color.Black * 0.5f, 10, GUI.Font);
}
foreach (Limb limb in character.AnimController.Limbs)
@@ -4450,11 +4452,11 @@ namespace Barotrauma.CharacterEditor
}
if (editJoints)
{
if (altDown && joint.BodyA == limb.body.FarseerBody)
if (!altDown && joint.BodyA == limb.body.FarseerBody)
{
continue;
}
if (!altDown && joint.BodyB == limb.body.FarseerBody)
if (altDown && joint.BodyB == limb.body.FarseerBody)
{
continue;
}
@@ -4465,7 +4467,13 @@ namespace Barotrauma.CharacterEditor
{
if (joint.LimitEnabled && jointCreationMode == JointCreationMode.None)
{
DrawJointLimitWidgets(spriteBatch, limb, joint, tformedJointPos, autoFreeze: true, allowPairEditing: true, rotationOffset: limb.Rotation, holdPosition: true);
var otherBody = limb == joint.LimbA ? joint.LimbB : joint.LimbA;
float rotation = -otherBody.Rotation + limb.Params.GetSpriteOrientation();
if (character.AnimController.Dir < 0)
{
rotation -= MathHelper.Pi;
}
DrawJointLimitWidgets(spriteBatch, limb, joint, tformedJointPos, autoFreeze: true, allowPairEditing: true, rotationOffset: rotation, holdPosition: true);
}
// Is the direction inversed incorrectly?
Vector2 to = tformedJointPos + VectorExtensions.ForwardFlipped(joint.LimbB.Rotation - joint.LimbB.Params.GetSpriteOrientation(), 20);
@@ -4898,7 +4906,7 @@ namespace Barotrauma.CharacterEditor
void RecalculateCollider(Limb l)
{
// We want the collider to be slightly smaller than the source rect, because the source rect is usually a bit bigger than the graphic.
float multiplier = 0.85f;
float multiplier = 0.9f;
l.body.SetSize(new Vector2(ConvertUnits.ToSimUnits(width), ConvertUnits.ToSimUnits(height)) * l.Scale * RagdollParams.TextureScale * multiplier);
TryUpdateLimbParam(l, "radius", ConvertUnits.ToDisplayUnits(l.body.radius / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
TryUpdateLimbParam(l, "width", ConvertUnits.ToDisplayUnits(l.body.width / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
@@ -5010,7 +5018,7 @@ namespace Barotrauma.CharacterEditor
{
if (joint.LimitEnabled && jointCreationMode == JointCreationMode.None)
{
DrawJointLimitWidgets(spriteBatch, limb, joint, tformedJointPos, autoFreeze: false, allowPairEditing: true, holdPosition: false);
DrawJointLimitWidgets(spriteBatch, limb, joint, tformedJointPos, autoFreeze: false, allowPairEditing: true, holdPosition: false, rotationOffset: joint.LimbB.Params.GetSpriteOrientation());
}
if (jointSelectionWidget.IsControlled)
{
@@ -5071,7 +5079,7 @@ namespace Barotrauma.CharacterEditor
private void DrawJointLimitWidgets(SpriteBatch spriteBatch, Limb limb, LimbJoint joint, Vector2 drawPos, bool autoFreeze, bool allowPairEditing, bool holdPosition, float rotationOffset = 0)
{
rotationOffset -= limb.Params.GetSpriteOrientation();
bool clockWise = joint.Params.ClockWiseRotation;
Color angleColor = joint.UpperLimit - joint.LowerLimit > 0 ? GUI.Style.Green * 0.5f : GUI.Style.Red;
DrawRadialWidget(spriteBatch, drawPos, MathHelper.ToDegrees(joint.UpperLimit), $"{joint.Params.Name}: {GetCharacterEditorTranslation("UpperLimit")}", Color.Cyan, angle =>
{
@@ -5111,7 +5119,7 @@ namespace Barotrauma.CharacterEditor
DrawAngle(20, angleColor, 4);
DrawAngle(40, Color.Cyan);
GUI.DrawString(spriteBatch, drawPos, angle.FormatZeroDecimal(), Color.Black, backgroundColor: Color.Cyan, font: GUI.SmallFont);
}, circleRadius: 40, rotationOffset: rotationOffset, displayAngle: false, clockWise: false, holdPosition: holdPosition);
}, circleRadius: 40, rotationOffset: rotationOffset, displayAngle: false, clockWise: clockWise, holdPosition: holdPosition);
DrawRadialWidget(spriteBatch, drawPos, MathHelper.ToDegrees(joint.LowerLimit), $"{joint.Params.Name}: {GetCharacterEditorTranslation("LowerLimit")}", Color.Yellow, angle =>
{
joint.LowerLimit = MathHelper.ToRadians(angle);
@@ -5150,12 +5158,12 @@ namespace Barotrauma.CharacterEditor
DrawAngle(20, angleColor, 4);
DrawAngle(25, Color.Yellow);
GUI.DrawString(spriteBatch, drawPos, angle.FormatZeroDecimal(), Color.Black, backgroundColor: Color.Yellow, font: GUI.SmallFont);
}, circleRadius: 25, rotationOffset: rotationOffset, displayAngle: false, clockWise: false, holdPosition: holdPosition);
}, circleRadius: 25, rotationOffset: rotationOffset, displayAngle: false, clockWise: clockWise, holdPosition: holdPosition);
void DrawAngle(float radius, Color color, float thickness = 5)
{
float angle = joint.UpperLimit - joint.LowerLimit;
ShapeExtensions.DrawSector(spriteBatch, drawPos, radius, angle, 40, color,
offset: -rotationOffset - joint.UpperLimit + MathHelper.PiOver2, thickness: thickness);
float offset = clockWise ? rotationOffset + joint.LowerLimit - MathHelper.PiOver2 : rotationOffset - joint.UpperLimit - MathHelper.PiOver2;
ShapeExtensions.DrawSector(spriteBatch, drawPos, radius, angle, 40, color, offset: offset, thickness: thickness);
}
}
@@ -5244,8 +5252,8 @@ namespace Barotrauma.CharacterEditor
{
angle = 0;
}
float drawAngle = clockWise ? -angle : angle;
var widgetDrawPos = drawPos + VectorExtensions.ForwardFlipped(MathHelper.ToRadians(drawAngle) + rotationOffset, circleRadius);
float drawAngle = clockWise ? angle : -angle;
var widgetDrawPos = drawPos + VectorExtensions.Forward(MathHelper.ToRadians(drawAngle) + rotationOffset - MathHelper.PiOver2, circleRadius);
GUI.DrawLine(spriteBatch, drawPos, widgetDrawPos, color);
DrawWidget(spriteBatch, widgetDrawPos, WidgetType.Rectangle, widgetSize, color, toolTip, () =>
{
@@ -5253,8 +5261,8 @@ namespace Barotrauma.CharacterEditor
ShapeExtensions.DrawCircle(spriteBatch, drawPos, circleRadius, 40, color, thickness: 1);
Vector2 d = PlayerInput.MousePosition - drawPos;
float newAngle = clockWise
? MathUtils.VectorToAngle(d) - MathHelper.PiOver2 + rotationOffset
: -MathUtils.VectorToAngle(d) + MathHelper.PiOver2 - rotationOffset;
? MathUtils.VectorToAngle(d) + MathHelper.PiOver2 - rotationOffset
: -MathUtils.VectorToAngle(d) - MathHelper.PiOver2 + rotationOffset;
angle = MathHelper.ToDegrees(wrapAnglePi ? MathUtils.WrapAnglePi(newAngle) : MathUtils.WrapAngleTwoPi(newAngle));
angle = (float)Math.Round(angle / rounding) * rounding;
if (angle >= 360 || angle <= -360) { angle = 0; }
@@ -5263,7 +5271,7 @@ namespace Barotrauma.CharacterEditor
GUI.DrawString(spriteBatch, drawPos, angle.FormatZeroDecimal(), Color.Black, backgroundColor: color, font: GUI.SmallFont);
}
onClick(angle);
var zeroPos = drawPos + VectorExtensions.ForwardFlipped(rotationOffset, circleRadius);
var zeroPos = drawPos + VectorExtensions.Forward(rotationOffset - MathHelper.PiOver2, circleRadius);
GUI.DrawLine(spriteBatch, drawPos, zeroPos, GUI.Style.Red, width: 3);
}, autoFreeze, holdPosition, onHovered: () =>
{
@@ -335,6 +335,7 @@ namespace Barotrauma
{
base.Select();
GUI.PreventPauseMenuToggle = false;
pointerLightSource = new LightSource(Vector2.Zero, 1000.0f, Color.White, submarine: null);
GameMain.LightManager.AddLight(pointerLightSource);
topPanel.ClearChildren();
@@ -424,6 +424,8 @@ namespace Barotrauma
#region Selection
public override void Select()
{
GUI.PreventPauseMenuToggle = false;
base.Select();
if (GameMain.Client != null)
@@ -806,19 +808,6 @@ namespace Barotrauma
return true;
}
private bool JoinServerClicked(GUIButton button, object obj)
{
GameMain.ServerListScreen.Select();
return true;
}
private bool SteamWorkshopClicked(GUIButton button, object obj)
{
if (!Steam.SteamManager.IsInitialized) { return false; }
GameMain.SteamWorkshopScreen.Select();
return true;
}
private bool ChangeMaxPlayers(GUIButton button, object obj)
{
int.TryParse(maxPlayersBox.Text, out int currMaxPlayers);
@@ -829,33 +818,10 @@ namespace Barotrauma
return true;
}
private bool HostServerClicked(GUIButton button, object obj)
private void StartServer()
{
string name = serverNameBox.Text;
if (string.IsNullOrEmpty(name))
{
serverNameBox.Flash();
return false;
}
/*if (!int.TryParse(portBox.Text, out int port) || port < 0 || port > 65535)
{
portBox.Text = NetConfig.DefaultPort.ToString();
portBox.Flash();
return false;
}
int queryPort = 0;
#if USE_STEAM
if (!int.TryParse(queryPortBox.Text, out queryPort) || queryPort < 0 || queryPort > 65535)
{
portBox.Text = NetConfig.DefaultQueryPort.ToString();
portBox.Flash();
return false;
}
#endif
*/
GameMain.NetLobbyScreen?.Release();
GameMain.NetLobbyScreen = new NetLobbyScreen();
try
@@ -919,8 +885,6 @@ namespace Barotrauma
{
DebugConsole.ThrowError("Failed to start server", e);
}
return true;
}
private bool QuitClicked(GUIButton button, object obj)
@@ -996,7 +960,7 @@ namespace Barotrauma
GUI.Draw(Cam, spriteBatch);
#if !UNSTABLE
string versionString = "Barotrauma v" + GameMain.Version + " (" + AssemblyInfo.GetBuildString() + ", branch " + AssemblyInfo.GetGitBranch() + ", revision " + AssemblyInfo.GetGitRevision() + ")";
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)
@@ -1062,7 +1026,7 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce(
"MainMenuScreen.StartGame:IOException" + selectedSub.Name,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Copying the file \"" + selectedSub.FilePath + "\" failed.\n" + e.Message + "\n" + Environment.StackTrace);
"Copying the file \"" + selectedSub.FilePath + "\" failed.\n" + e.Message + "\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
@@ -1334,7 +1298,35 @@ namespace Barotrauma
new GUIButton(new RectTransform(new Vector2(0.4f, 0.07f), content.RectTransform), TextManager.Get("StartServerButton"), style: "GUIButtonLarge")
{
OnClicked = HostServerClicked
OnClicked = (btn, userdata) =>
{
string name = serverNameBox.Text;
if (string.IsNullOrEmpty(name))
{
serverNameBox.Flash();
return false;
}
if (ForbiddenWordFilter.IsForbidden(name, out string forbiddenWord))
{
var msgBox = new GUIMessageBox("",
TextManager.GetWithVariables("forbiddenservernameverification", new string[] { "[forbiddenword]", "[servername]" }, new string[] { forbiddenWord, name }),
new string[] { TextManager.Get("yes"), TextManager.Get("no") });
msgBox.Buttons[0].OnClicked += (_, __) =>
{
StartServer();
msgBox.Close();
return true;
};
msgBox.Buttons[1].OnClicked += msgBox.Close;
}
else
{
StartServer();
}
return true;
}
};
}
@@ -1169,7 +1169,7 @@ namespace Barotrauma
Character.Controlled = null;
GameMain.LightManager.LosEnabled = false;
GUI.PreventPauseMenuToggle = false;
CampaignCharacterDiscarded = false;
chatInput.Select();
@@ -2799,7 +2799,7 @@ namespace Barotrauma
var variantButton = CreateJobVariantButton(jobPrefab, variantIndex, images.Length, jobButton);
variantButton.OnClicked = (btn, obj) =>
{
currSelected.Selected = false;
if (currSelected != null) { currSelected.Selected = false; }
int k = ((Pair<JobPrefab, int>)obj).Second;
btn.Parent.UserData = obj;
for (int j = 0; j < images.Length; j++)
@@ -153,6 +153,7 @@ namespace Barotrauma
private GUITickBox filterVoip;
private List<GUITickBox> playStyleTickBoxes;
private List<GUITickBox> gameModeTickBoxes;
private GUITickBox filterOffensive;
private string sortedBy;
@@ -325,7 +326,7 @@ namespace Barotrauma
filterSameVersion = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterSameVersion"))
{
ToolTip = TextManager.Get("FilterSameVersion"),
UserData = TextManager.Get("FilterSameVersion"),
Selected = true,
OnSelected = (tickBox) => { FilterServers(); return true; }
};
@@ -333,39 +334,47 @@ namespace Barotrauma
filterPassword = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterPassword"))
{
ToolTip = TextManager.Get("FilterPassword"),
UserData = TextManager.Get("FilterPassword"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterPassword.TextBlock);
filterIncompatible = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterIncompatibleServers"))
{
ToolTip = TextManager.Get("FilterIncompatibleServers"),
UserData = TextManager.Get("FilterIncompatibleServers"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterIncompatible.TextBlock);
filterFull = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterFullServers"))
{
ToolTip = TextManager.Get("FilterFullServers"),
UserData = TextManager.Get("FilterFullServers"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterFull.TextBlock);
filterEmpty = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterEmptyServers"))
{
ToolTip = TextManager.Get("FilterEmptyServers"),
UserData = TextManager.Get("FilterEmptyServers"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterEmpty.TextBlock);
filterWhitelisted = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterWhitelistedServers"))
{
ToolTip = TextManager.Get("FilterWhitelistedServers"),
UserData = TextManager.Get("FilterWhitelistedServers"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterWhitelisted.TextBlock);
filterOffensive = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterOffensiveServers"))
{
UserData = TextManager.Get("FilterOffensiveServers"),
ToolTip = TextManager.Get("FilterOffensiveServersToolTip"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterOffensive.TextBlock);
// Filter Tags
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), filters.Content.RectTransform), TextManager.Get("servertags"), font: GUI.SubHeadingFont)
{
@@ -374,35 +383,35 @@ namespace Barotrauma
filterKarma = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.karma.true"))
{
ToolTip = TextManager.Get("servertag.karma.true"),
UserData = TextManager.Get("servertag.karma.true"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterKarma.TextBlock);
filterTraitor = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.traitors.true"))
{
ToolTip = TextManager.Get("servertag.traitors.true"),
UserData = TextManager.Get("servertag.traitors.true"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterTraitor.TextBlock);
filterFriendlyFire = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.friendlyfire.false"))
{
ToolTip = TextManager.Get("servertag.friendlyfire.false"),
UserData = TextManager.Get("servertag.friendlyfire.false"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterFriendlyFire.TextBlock);
filterVoip = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.voip.false"))
{
ToolTip = TextManager.Get("servertag.voip.false"),
UserData = TextManager.Get("servertag.voip.false"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterVoip.TextBlock);
filterModded = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.modded.true"))
{
ToolTip = TextManager.Get("servertag.modded.true"),
UserData = TextManager.Get("servertag.modded.true"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTextList.Add(filterModded.TextBlock);
@@ -449,7 +458,9 @@ namespace Barotrauma
filters.Content.RectTransform.SizeChanged += () =>
{
filters.Content.RectTransform.RecalculateChildren(true, true);
filterTextList.ForEach(t => t.Text = t.ToolTip);
filterTextList.ForEach(t => t.Text = t.Parent.Parent.UserData as string);
gameModeTickBoxes.ForEach(tb => tb.Text = tb.ToolTip);
playStyleTickBoxes.ForEach(tb => tb.Text = tb.ToolTip);
GUITextBlock.AutoScaleAndNormalize(filterTextList, defaultScale: 1.0f);
if (filterTextList[0].TextScale < 0.8f)
{
@@ -579,7 +590,7 @@ namespace Barotrauma
{
ClientNameBox.Flash();
ClientNameBox.Select();
GUI.PlayUISound(GUISoundType.PickItemFail);
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
return false;
}
ShowDirectJoinPrompt();
@@ -1050,6 +1061,7 @@ namespace Barotrauma
(!filterFull.Selected || serverInfo.PlayerCount < serverInfo.MaxPlayers) &&
(!filterEmpty.Selected || serverInfo.PlayerCount > 0) &&
(!filterWhitelisted.Selected || serverInfo.UsingWhiteList == true) &&
(filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) &&
(!filterKarma.Selected || serverInfo.KarmaEnabled == true) &&
(!filterFriendlyFire.Selected || serverInfo.FriendlyFireEnabled == false) &&
(!filterTraitor.Selected || serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe) &&
@@ -1406,7 +1418,7 @@ namespace Barotrauma
#if DEBUG
DebugConsole.ThrowError($"Failed to parse a Steam friend's connect command ({connectCommand})", e);
#else
DebugConsole.Log($"Failed to parse a Steam friend's connect command ({connectCommand})\n" + e.StackTrace);
DebugConsole.Log($"Failed to parse a Steam friend's connect command ({connectCommand})\n" + e.StackTrace.CleanupStackTrace());
#endif
info.ConnectName = null;
info.ConnectEndpoint = null;
@@ -2109,7 +2121,7 @@ namespace Barotrauma
{
ClientNameBox.Flash();
ClientNameBox.Select();
GUI.PlayUISound(GUISoundType.PickItemFail);
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
return false;
}
@@ -1156,7 +1156,7 @@ namespace Barotrauma
if (itemContentPackage == null)
{
string errorMsg = "Failed to edit workshop item (content package null)\n" + Environment.StackTrace;
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);
return;
@@ -1,4 +1,4 @@
using Barotrauma.Extensions;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -33,6 +33,18 @@ namespace Barotrauma
Default,
Wiring
}
public enum WarningType
{
NoWaypoints,
NoHulls,
DisconnectedVents,
NoHumanSpawnpoints,
NoCargoSpawnpoints,
NoBallastTag,
NonLinkedGaps,
TooManyLights
}
public static Vector2 MouseDragStart = Vector2.Zero;
@@ -85,11 +97,19 @@ namespace Barotrauma
private GUIFrame previouslyUsedPanel;
private GUIListBox previouslyUsedList;
private GUIFrame undoBufferPanel;
private GUIListBox undoBufferList;
private GUIDropDown linkedSubBox;
private static GUIComponent autoSaveLabel;
private static int maxAutoSaves = GameSettings.MaximumAutoSaves;
public static bool BulkItemBufferInUse;
public static List<AddOrDeleteCommand> BulkItemBuffer = new List<AddOrDeleteCommand>();
public static List<WarningType> SuppressedWarnings = new List<WarningType>();
//a Character used for picking up and manipulating items
private Character dummyCharacter;
@@ -109,12 +129,17 @@ namespace Barotrauma
/// </summary>
private Vector2 oldItemPosition;
/// <summary>
/// Global undo/redo state for the sub editor and a selector index for it
/// <see cref="Command"/>
/// </summary>
public static readonly List<Command> Commands = new List<Command>();
private static int commandIndex;
private GUIFrame wiringToolPanel;
private DateTime editorSelectedTime;
private const string containerDeleteTag = "containerdelete";
private GUIImage previewImage;
private GUILayoutGroup previewImageButtonHolder;
@@ -273,6 +298,7 @@ namespace Barotrauma
OnClicked = (btn, userData) =>
{
previouslyUsedPanel.Visible = false;
undoBufferPanel.Visible = false;
showEntitiesPanel.Visible = !showEntitiesPanel.Visible;
showEntitiesPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(Math.Max(btn.Rect.X, entityCountPanel.Rect.Right), saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
return true;
@@ -285,12 +311,26 @@ namespace Barotrauma
OnClicked = (btn, userData) =>
{
showEntitiesPanel.Visible = false;
undoBufferPanel.Visible = false;
previouslyUsedPanel.Visible = !previouslyUsedPanel.Visible;
previouslyUsedPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(Math.Max(btn.Rect.X, entityCountPanel.Rect.Right), saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
return true;
}
};
var undoBufferButton = new GUIButton(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "UndoHistoryButton")
{
ToolTip = TextManager.Get("Editor.UndoHistoryButton"),
OnClicked = (btn, userData) =>
{
showEntitiesPanel.Visible = false;
previouslyUsedPanel.Visible = false;
undoBufferPanel.Visible = !undoBufferPanel.Visible;
undoBufferPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(Math.Max(btn.Rect.X, entityCountPanel.Rect.Right), saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
return true;
}
};
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), paddedTopPanel.RectTransform), style: "VerticalLine");
subNameLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft),
@@ -407,6 +447,45 @@ namespace Barotrauma
//-----------------------------------------------
undoBufferPanel = new GUIFrame(new RectTransform(new Vector2(0.15f, 0.2f), GUI.Canvas) { MinSize = new Point(200, 200) })
{
Visible = false
};
undoBufferList = new GUIListBox(new RectTransform(new Vector2(0.925f, 0.9f), undoBufferPanel.RectTransform, Anchor.Center))
{
ScrollBarVisible = true,
OnSelected = (_, userData) =>
{
int index;
if (userData is Command command)
{
index = Commands.IndexOf(command);
}
else
{
index = -1;
}
int diff = index- commandIndex;
int amount = Math.Abs(diff);
if (diff >= 0)
{
Redo(amount + 1);
}
else
{
Undo(amount - 1);
}
return true;
}
};
UpdateUndoHistoryPanel();
//-----------------------------------------------
showEntitiesPanel = new GUIFrame(new RectTransform(new Vector2(0.08f, 0.5f), GUI.Canvas)
{
MinSize = new Point(170, 0)
@@ -885,6 +964,7 @@ namespace Barotrauma
{
base.Select();
GUI.PreventPauseMenuToggle = false;
if (!Directory.Exists(autoSavePath))
{
System.IO.DirectoryInfo e = Directory.CreateDirectory(autoSavePath);
@@ -1043,6 +1123,7 @@ namespace Barotrauma
MapEntity.DeselectAll();
MapEntity.SelectionGroups.Clear();
ClearUndoBuffer();
SetMode(Mode.Default);
@@ -1062,147 +1143,15 @@ namespace Barotrauma
GameMain.World.ProcessChanges();
}
if (GUIMessageBox.MessageBoxes.Any(mbox => (mbox as GUIMessageBox)?.Tag == containerDeleteTag))
{
for (int i = 0; i < GUIMessageBox.MessageBoxes.Count; i++)
{
GUIMessageBox box = GUIMessageBox.MessageBoxes[i] as GUIMessageBox;
if (box != null && box.Tag != containerDeleteTag) continue;
box?.Close();
i--; // Take into account the message boxes removing themselves from the list when closed
}
}
ClearFilter();
}
public void HandleContainerContentsDeletion(Item itemToDelete, Inventory itemInventory)
{
string itemNames = string.Empty;
foreach (Item item in itemInventory.Items)
{
if (item == null) continue;
itemNames += item.Name + "\n";
}
if (itemNames.Length > 0)
{
// Multiple prompts open
if (GUIMessageBox.MessageBoxes.Any(mbox => (mbox as GUIMessageBox)?.Tag == containerDeleteTag))
{
var msgBox = new GUIMessageBox(itemToDelete.Name, TextManager.Get("DeletingContainerWithItems") + itemNames, new[] { TextManager.Get("Yes"), TextManager.Get("No"), TextManager.Get("YesToAll"), TextManager.Get("NoToAll") }, tag: containerDeleteTag);
// Yes
msgBox.Buttons[0].OnClicked = (btn, userdata) =>
{
itemInventory.DeleteAllItems();
msgBox.Close();
return true;
};
// No
msgBox.Buttons[1].OnClicked = (btn, userdata) =>
{
if (Selected == GameMain.SubEditorScreen)
{
foreach (Item item in itemInventory.Items)
{
item?.Drop(null);
}
}
else // If current screen is not subeditor, delete anyway to avoid lingering objects
{
itemInventory.DeleteAllItems();
}
msgBox.Close();
return true;
};
// Yes to All
msgBox.Buttons[2].OnClicked = (btn, userdata) =>
{
for (int i = 0; i < GUIMessageBox.MessageBoxes.Count; i++)
{
GUIMessageBox box = GUIMessageBox.MessageBoxes[i] as GUIMessageBox;
if (box?.Tag != msgBox.Tag || box == msgBox) continue;
GUIButton button = box?.Buttons[0];
button?.OnClicked(button, button.UserData);
i--; // Take into account the message boxes removing themselves from the list when closed
}
itemInventory.DeleteAllItems();
msgBox.Close();
return true;
};
// No to all
msgBox.Buttons[3].OnClicked = (btn, userdata) =>
{
for (int i = 0; i < GUIMessageBox.MessageBoxes.Count; i++)
{
GUIMessageBox box = GUIMessageBox.MessageBoxes[i] as GUIMessageBox;
if (box?.Tag != msgBox.Tag || box == msgBox) continue;
GUIButton button = box?.Buttons[1];
button?.OnClicked(button, button.UserData);
i--; // Take into account the message boxes removing themselves from the list when closed
}
if (Selected == GameMain.SubEditorScreen)
{
foreach (Item item in itemInventory.Items)
{
item?.Drop(null);
}
}
else // If current screen is not subeditor, delete anyway to avoid lingering objects
{
itemInventory.DeleteAllItems();
}
msgBox.Close();
return true;
};
}
else // Single prompt
{
var msgBox = new GUIMessageBox(itemToDelete.Name, TextManager.Get("DeletingContainerWithItems") + itemNames, new[] { TextManager.Get("Yes"), TextManager.Get("No") }, tag: containerDeleteTag);
// Yes
msgBox.Buttons[0].OnClicked = (btn, userdata) =>
{
itemInventory.DeleteAllItems();
msgBox.Close();
return true;
};
// No
msgBox.Buttons[1].OnClicked = (btn, userdata) =>
{
if (Selected == GameMain.SubEditorScreen)
{
foreach (Item item in itemInventory.Items)
{
item?.Drop(null);
}
}
else // If current screen is not subeditor, delete anyway to avoid lingering objects
{
itemInventory.DeleteAllItems();
}
msgBox.Close();
return true;
};
}
}
}
private void CreateDummyCharacter()
{
if (dummyCharacter != null) RemoveDummyCharacter();
dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", hasAi: false);
dummyCharacter.Info.Name = "Galldren";
//make space for the entity menu
for (int i = 0; i < dummyCharacter.Inventory.SlotPositions.Length; i++)
@@ -2609,7 +2558,7 @@ namespace Barotrauma
var selectedSub = new Submarine(selectedSubInfo);
Submarine.MainSub = selectedSub;
Submarine.MainSub.UpdateTransform(interpolate: false);
ClearUndoBuffer();
CreateDummyCharacter();
string name = Submarine.MainSub.Info.Name;
@@ -2790,6 +2739,7 @@ namespace Barotrauma
MapEntity.DeselectAll();
MapEntity.FilteredSelectedList.Clear();
ClearUndoBuffer();
CreateDummyCharacter();
if (newMode == Mode.Wiring)
@@ -2922,7 +2872,7 @@ namespace Barotrauma
CreateBackgroundColorPicker();
break;
case "selectsame":
IEnumerable<MapEntity> matching = MapEntity.mapEntityList.Where(e => targets.Any(t => t.prefab.Identifier == e.prefab.Identifier) && !MapEntity.SelectedList.Contains(e));
IEnumerable<MapEntity> matching = MapEntity.mapEntityList.Where(e => e.prefab != null && targets.Any(t => t.prefab.Identifier == e.prefab.Identifier) && !MapEntity.SelectedList.Contains(e));
MapEntity.SelectedList.AddRange(matching);
break;
case "copy":
@@ -2935,7 +2885,8 @@ namespace Barotrauma
MapEntity.Paste(cam.ScreenToWorld(contextMenu.Rect.Location.ToVector2()));
break;
case "delete":
targets.ForEach(me => { me.Remove(); });
StoreCommand(new AddOrDeleteCommand(targets, true));
targets.ForEach(me => { if (!me.Removed) { me.Remove(); }});
break;
case "open" when target != null:
OpenItem(target);
@@ -3234,7 +3185,13 @@ namespace Barotrauma
}
}
});
GUI.PlayUISound(spawnedItem ? GUISoundType.PickItem : GUISoundType.PickItemFail);
List<MapEntity> placedEntities = itemInstance.Where(it => !it.Removed).Cast<MapEntity>().ToList();
if (placedEntities.Any())
{
StoreCommand(new AddOrDeleteCommand(placedEntities, false));
}
SoundPlayer.PlayUISound(spawnedItem ? GUISoundType.PickItem : GUISoundType.PickItemFail);
break;
}
case ItemPrefab itemPrefab when PlayerInput.IsShiftDown():
@@ -3243,12 +3200,17 @@ namespace Barotrauma
if (!inv.TryPutItem(item, dummyCharacter))
{
// We failed, remove the item so it doesn't stay at x:0,y:0
GUI.PlayUISound(GUISoundType.PickItemFail);
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
item.Remove();
}
else
{
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
}
if (!item.Removed)
{
StoreCommand(new AddOrDeleteCommand(new List<MapEntity> { item }, false));
}
break;
}
@@ -3257,7 +3219,7 @@ namespace Barotrauma
{
// Place the item into our hands
DraggedItemPrefab = (MapEntityPrefab) obj;
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
break;
}
}
@@ -3265,7 +3227,7 @@ namespace Barotrauma
}
else
{
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
MapEntityPrefab.SelectPrefab(obj);
}
@@ -3611,6 +3573,7 @@ namespace Barotrauma
EntityMenu.AddToGUIUpdateList();
showEntitiesPanel.AddToGUIUpdateList();
previouslyUsedPanel.AddToGUIUpdateList();
undoBufferPanel.AddToGUIUpdateList();
entityCountPanel.AddToGUIUpdateList();
TopPanel.AddToGUIUpdateList();
@@ -3655,10 +3618,7 @@ namespace Barotrauma
/// </summary>
private bool IsMouseOnEditorGUI()
{
if (GUI.MouseOn == null)
{
return false;
}
if (GUI.MouseOn == null) { return false; }
return (EntityMenu?.MouseRect.Contains(PlayerInput.MousePosition) ?? false)
|| (entityCountPanel?.MouseRect.Contains(PlayerInput.MousePosition) ?? false)
@@ -3666,6 +3626,91 @@ namespace Barotrauma
|| (TopPanel?.MouseRect.Contains(PlayerInput.MousePosition) ?? false);
}
private static void Redo(int amount)
{
for (int i = 0; i < amount; i++)
{
if (commandIndex < Commands.Count)
{
Command command = Commands[commandIndex++];
command.Execute();
}
}
GameMain.SubEditorScreen.UpdateUndoHistoryPanel();
}
private static void Undo(int amount)
{
for (int i = 0; i < amount; i++)
{
if (commandIndex > 0)
{
Command command = Commands[--commandIndex];
command.UnExecute();
}
}
GameMain.SubEditorScreen.UpdateUndoHistoryPanel();
}
private static void ClearUndoBuffer()
{
SerializableEntityEditor.PropertyChangesActive = false;
SerializableEntityEditor.CommandBuffer = null;
Commands.ForEach(cmd => cmd.Cleanup());
Commands.Clear();
commandIndex = 0;
GameMain.SubEditorScreen.UpdateUndoHistoryPanel();
}
public static void StoreCommand(Command command)
{
if (commandIndex != Commands.Count)
{
Commands.RemoveRange(commandIndex, Commands.Count - commandIndex);
}
Commands.Add(command);
commandIndex++;
// Start removing old commands
if (Commands.Count > Math.Clamp(GameSettings.SubEditorMaxUndoBuffer, 1, 10240))
{
Commands.First()?.Cleanup();
Commands.RemoveRange(0, 1);
commandIndex = Commands.Count;
}
GameMain.SubEditorScreen.UpdateUndoHistoryPanel();
}
public void UpdateUndoHistoryPanel()
{
if (undoBufferPanel == null) { return; }
undoBufferList.Content.Children.ForEachMod(component =>
{
undoBufferList.Content.RemoveChild(component);
});
for (int i = 0; i < Commands.Count; i++)
{
Command command = Commands[i];
string description = command.GetDescription();
CreateTextBlock(description, description, i + 1, command).RectTransform.SetAsFirstChild();
}
CreateTextBlock(TextManager.Get("undo.beginning"), TextManager.Get("undo.beginningtooltip"), 0, null);
GUITextBlock CreateTextBlock(string name, string description, int index, Command command)
{
return new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), undoBufferList.Content.RectTransform) { MinSize = new Point(0, 15) },
ToolBox.LimitString(name, GUI.SmallFont, undoBufferList.Content.Rect.Width), font: GUI.SmallFont, textColor: index == commandIndex ? GUI.Style.Green : (Color?) null)
{
UserData = command,
ToolTip = description
};
}
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
@@ -3745,7 +3790,32 @@ namespace Barotrauma
}
}
}
if (undoBufferPanel.Visible)
{
undoBufferList.Deselect();
}
if (GUI.KeyboardDispatcher.Subscriber == null
|| MapEntity.EditingHUD != null
&& GUI.KeyboardDispatcher.Subscriber is GUIComponent sub
&& MapEntity.EditingHUD.Children.Contains(sub))
{
if (PlayerInput.IsCtrlDown() && !WiringMode)
{
if (PlayerInput.KeyHit(Keys.Z))
{
// Ctrl+Shift+Z redos while Ctrl+Z undos
if (PlayerInput.IsShiftDown()) { Redo(1); } else { Undo(1); }
}
// ctrl+Y redo
if (PlayerInput.KeyHit(Keys.Y))
{
Redo(1);
}
}
}
if (GUI.KeyboardDispatcher.Subscriber == null)
{
@@ -4019,9 +4089,15 @@ namespace Barotrauma
newItem.Remove();
}
if (!newItem.Removed)
{
BulkItemBufferInUse = true;
BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { newItem }, false));
}
if (!dragginMouse)
{
GUI.PlayUISound(spawnedItem ? GUISoundType.PickItem : GUISoundType.PickItemFail);
SoundPlayer.PlayUISound(spawnedItem ? GUISoundType.PickItem : GUISoundType.PickItemFail);
}
}
}
@@ -4083,14 +4159,41 @@ namespace Barotrauma
}
}
}
List<MapEntity> placedEntities = itemInstance.Where(it => !it.Removed).Cast<MapEntity>().ToList();
if (placedEntities.Any())
{
BulkItemBufferInUse = true;
BulkItemBuffer.Add(new AddOrDeleteCommand(placedEntities, false));
}
}
}
GUI.PlayUISound(spawnedItems ? GUISoundType.PickItem : GUISoundType.PickItemFail);
SoundPlayer.PlayUISound(spawnedItems ? GUISoundType.PickItem : GUISoundType.PickItemFail);
break;
}
}
}
if (BulkItemBufferInUse && PlayerInput.PrimaryMouseButtonReleased() && BulkItemBuffer.Any())
{
AddOrDeleteCommand master = BulkItemBuffer[0];
for (int i = 1; i < BulkItemBuffer.Count; i++)
{
AddOrDeleteCommand command = BulkItemBuffer[i];
command.MergeInto(master);
}
StoreCommand(master);
BulkItemBuffer.Clear();
BulkItemBufferInUse = false;
}
if (SerializableEntityEditor.PropertyChangesActive && (SerializableEntityEditor.NextCommandPush < DateTime.Now || MapEntity.EditingHUD == null))
{
SerializableEntityEditor.CommitCommandBuffer();
}
// Update our mouse dragging state so we can easily slide thru slots while holding the mouse button down to place lots of items
if (PlayerInput.PrimaryMouseButtonHeld())
{
@@ -18,6 +18,11 @@ namespace Barotrauma
public static List<string> MissingLocalizations = new List<string>();
#endif
public static bool LockEditing;
public static bool PropertyChangesActive;
public static DateTime NextCommandPush;
public static Tuple<SerializableProperty, PropertyCommand> CommandBuffer;
public int ContentHeight
{
get
@@ -1091,10 +1096,67 @@ namespace Barotrauma
private bool SetPropertyValue(SerializableProperty property, object entity, object value)
{
MultiSetProperties(property, entity, value);
if (LockEditing) { return false; }
object oldData = property.GetValue(entity);
// some properties have null as the default string value
if (oldData == null && value is string) { oldData = ""; }
if (entity is ISerializableEntity sEntity && Screen.Selected is SubEditorScreen && !Equals(oldData, value))
{
List<ISerializableEntity> entities = new List<ISerializableEntity> { sEntity };
Dictionary<ISerializableEntity, object> affected = MultiSetProperties(property, entity, value);
Dictionary<object, List<ISerializableEntity>> oldValues = new Dictionary<object, List<ISerializableEntity>> {{ oldData, new List<ISerializableEntity> { sEntity }}};
affected.ForEach(aEntity =>
{
var (item, oldVal) = aEntity;
entities.Add(item);
if (!oldValues.ContainsKey(oldVal))
{
oldValues.Add(oldVal, new List<ISerializableEntity> { item });
}
else
{
oldValues[oldVal].Add(item);
}
});
PropertyCommand cmd = new PropertyCommand(entities, property.Name, value, oldValues);
if (CommandBuffer != null)
{
if (CommandBuffer.Item1 == property && CommandBuffer.Item2.PropertyCount == cmd.PropertyCount)
{
if (!CommandBuffer.Item2.MergeInto(cmd))
{
CommitCommandBuffer();
}
}
else
{
CommitCommandBuffer();
}
}
NextCommandPush = DateTime.Now.AddSeconds(1);
CommandBuffer = Tuple.Create(property, cmd);
PropertyChangesActive = true;
}
return property.TrySetValue(entity, value);
}
public static void CommitCommandBuffer()
{
if (CommandBuffer != null)
{
SubEditorScreen.StoreCommand(CommandBuffer.Item2);
}
CommandBuffer = null;
PropertyChangesActive = false;
}
/// <summary>
/// Sets common shared properties to all selected map entities in sub editor.
/// Only works client side while in the sub editor and when parentObject is ItemComponent, Item or Structure.
@@ -1103,10 +1165,12 @@ namespace Barotrauma
/// <param name="parentObject"></param>
/// <param name="value"></param>
/// <remarks>The function has the same parameters as <see cref="SetValue"/></remarks>
private void MultiSetProperties(SerializableProperty property, object parentObject, object value)
private Dictionary<ISerializableEntity, object> MultiSetProperties(SerializableProperty property, object parentObject, object value)
{
if (!(Screen.Selected is SubEditorScreen) || MapEntity.SelectedList.Count <= 1) { return; }
if (!(parentObject is ItemComponent || parentObject is Item || parentObject is Structure || parentObject is Hull)) { return; }
Dictionary<ISerializableEntity, object> affected = new Dictionary<ISerializableEntity, object>();
if (!(Screen.Selected is SubEditorScreen) || MapEntity.SelectedList.Count <= 1) { return affected; }
if (!(parentObject is ItemComponent || parentObject is Item || parentObject is Structure || parentObject is Hull)) { return affected; }
foreach (var entity in MapEntity.SelectedList.Where(entity => entity != parentObject))
{
@@ -1115,35 +1179,36 @@ namespace Barotrauma
case Hull _:
case Structure _:
case Item _:
{
if (entity.GetType() == parentObject.GetType())
{
{
affected.Add((ISerializableEntity) entity, property.GetValue(entity));
property.PropertyInfo.SetValue(entity, value);
}
else if (entity is ISerializableEntity sEntity && sEntity.SerializableProperties != null)
{
var props = sEntity.SerializableProperties;
if (props.TryGetValue(property.NameToLowerInvariant, out SerializableProperty foundProp))
{
affected.Add(sEntity, foundProp.GetValue(sEntity));
foundProp.PropertyInfo.SetValue(entity, value);
}
}
break;
}
case ItemComponent _ when entity is Item item:
{
foreach (var component in item.Components)
{
if (component.GetType() == parentObject.GetType() && component != parentObject)
{
{
affected.Add(component, property.GetValue(component));
property.PropertyInfo.SetValue(component, value);
}
}
break;
}
}
}
return affected;
}
}
}
@@ -275,7 +275,7 @@ namespace Barotrauma.Sounds
{
if (value < 0.25f || value > 4.0f)
{
DebugConsole.ThrowError($"Frequency multiplier out of range: {value}" + Environment.StackTrace);
DebugConsole.ThrowError($"Frequency multiplier out of range: {value}" + Environment.StackTrace.CleanupStackTrace());
}
frequencyMultiplier = Math.Clamp(value, 0.25f, 4.0f);
@@ -79,6 +79,11 @@ namespace Barotrauma
private static float ambientSoundTimer;
private static Vector2 ambientSoundInterval = new Vector2(20.0f, 40.0f); //x = min, y = max
private static SoundChannel hullSoundChannel;
private static Hull hullSoundSource;
private static float hullSoundTimer;
private static Vector2 hullSoundInterval = new Vector2(45.0f, 90.0f); //x = min, y = max
//misc
public static List<Sound> FlowSounds = new List<Sound>();
public static List<Sound> SplashSounds = new List<Sound>();
@@ -94,11 +99,16 @@ namespace Barotrauma
private static float[] fireVolumeRight;
const float FireSoundRange = 1000.0f;
const float FireSoundMediumLimit = 100.0f;
const float FireSoundLargeLimit = 200.0f; //switch to large fire sound when the size of a firesource is above this
const int fireSizes = 3;
private static string[] fireSoundTags = new string[fireSizes] { "fire", "firemedium", "firelarge" };
// TODO: could use a dictionary to split up the list into smaller lists of same type?
private static List<DamageSound> damageSounds;
private static Dictionary<GUISoundType, List<Sound>> guiSounds;
private static Sound startUpSound;
public static bool Initialized;
@@ -163,6 +173,7 @@ namespace Barotrauma
List<KeyValuePair<string, Sound>> miscSoundList = new List<KeyValuePair<string, Sound>>();
damageSounds ??= new List<DamageSound>();
musicClips ??= new List<BackgroundMusic>();
guiSounds ??= new Dictionary<GUISoundType, List<Sound>>();
bool firstWaterAmbienceLoaded = false;
@@ -250,6 +261,21 @@ namespace Barotrauma
damageSoundType,
soundElement.GetAttributeString("requiredtag", "")));
break;
case "guisound":
Sound guiSound = GameMain.SoundManager.LoadSound(soundElement, stream: false);
if (guiSound == null) { continue; }
if (Enum.TryParse(soundElement.GetAttributeString("guisoundtype", null), true, out GUISoundType soundType))
{
if (guiSounds.ContainsKey(soundType))
{
guiSounds[soundType].Add(guiSound);
}
else
{
guiSounds.Add(soundType, new List<Sound>() { guiSound });
}
}
break;
default:
Sound sound = GameMain.SoundManager.LoadSound(soundElement, false);
@@ -294,6 +320,15 @@ namespace Barotrauma
});
damageSounds.RemoveAll(s => s.sound.Disposed);
guiSounds.ForEach(kvp =>
{
kvp.Value?.ForEach(s =>
{
if (!soundElements.Any(e => SoundElementsEquivalent(s.XElement, e))) { s.Dispose(); }
});
});
guiSounds.ForEach(kvp => kvp.Value?.RemoveAll(s => s.Disposed));
miscSounds?.ForEach(g => g.ForEach(s =>
{
if (!soundElements.Any(e => SoundElementsEquivalent(s.XElement, e))) { s.Dispose(); }
@@ -305,9 +340,9 @@ namespace Barotrauma
flowVolumeLeft = new float[FlowSounds.Count];
flowVolumeRight = new float[FlowSounds.Count];
fireSoundChannels = new SoundChannel[2];
fireVolumeLeft = new float[2];
fireVolumeRight = new float[2];
fireSoundChannels = new SoundChannel[fireSizes];
fireVolumeLeft = new float[fireSizes];
fireVolumeRight = new float[fireSizes];
miscSounds = miscSoundList.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
@@ -319,7 +354,6 @@ namespace Barotrauma
}
public static void Update(float deltaTime)
{
if (!Initialized) { return; }
@@ -355,6 +389,12 @@ namespace Barotrauma
}
fireVolumeLeft[0] = 0.0f; fireVolumeLeft[1] = 0.0f;
fireVolumeRight[0] = 0.0f; fireVolumeRight[1] = 0.0f;
if (hullSoundChannel != null)
{
hullSoundChannel.FadeOutAndDispose();
hullSoundChannel = null;
hullSoundSource = null;
}
return;
}
@@ -372,6 +412,7 @@ namespace Barotrauma
UpdateWaterAmbience(ambienceVolume, deltaTime);
UpdateWaterFlowSounds(deltaTime);
UpdateRandomAmbience(deltaTime);
UpdateHullSounds(deltaTime);
UpdateFireSounds(deltaTime);
}
@@ -429,6 +470,9 @@ namespace Barotrauma
break;
}
// Consider the volume set in sounds.xml
if (sound != null) { volume *= sound.BaseGain; }
if ((waterAmbienceChannels[i] == null || !waterAmbienceChannels[i].IsPlaying) && volume > 0.01f)
{
waterAmbienceChannels[i] = sound.Play(volume, "waterambience");
@@ -490,6 +534,20 @@ namespace Barotrauma
}
}
if (Character.Controlled?.CharacterHealth?.GetAffliction("psychosis") is AfflictionPsychosis psychosis)
{
if (psychosis.CurrentFloodType == AfflictionPsychosis.FloodType.Minor)
{
targetFlowLeft[0] = Math.Max(targetFlowLeft[0], 1.0f);
targetFlowRight[0] = Math.Max(targetFlowRight[0], 1.0f);
}
else if (psychosis.CurrentFloodType == AfflictionPsychosis.FloodType.Major)
{
targetFlowLeft[FlowSounds.Count - 1] = Math.Max(targetFlowLeft[FlowSounds.Count - 1], 1.0f);
targetFlowRight[FlowSounds.Count - 1] = Math.Max(targetFlowRight[FlowSounds.Count - 1], 1.0f);
}
}
for (int i = 0; i < FlowSounds.Count; i++)
{
flowVolumeLeft[i] = (targetFlowLeft[i] < flowVolumeLeft[i]) ?
@@ -534,33 +592,11 @@ namespace Barotrauma
{
foreach (FireSource fs in hull.FireSources)
{
Vector2 diff = fs.WorldPosition + fs.Size / 2 - listenerPos;
if (Math.Abs(diff.X) < FireSoundRange && Math.Abs(diff.Y) < FireSoundRange)
{
Vector2 diffLeft = (fs.WorldPosition + new Vector2(fs.Size.X, fs.Size.Y / 2)) - listenerPos;
if (Math.Abs(diff.X) < fs.Size.X / 2.0f) { diffLeft.X = 0.0f; }
if (diffLeft.X <= 0)
{
float distFallOffLeft = diffLeft.Length() / FireSoundRange;
if (distFallOffLeft < 0.99f)
{
fireVolumeLeft[0] += (1.0f - distFallOffLeft);
if (fs.Size.X > FireSoundLargeLimit) fireVolumeLeft[1] += (1.0f - distFallOffLeft) * ((fs.Size.X - FireSoundLargeLimit) / FireSoundLargeLimit);
}
}
Vector2 diffRight = (fs.WorldPosition + new Vector2(0.0f, fs.Size.Y / 2)) - listenerPos;
if (Math.Abs(diff.X) < fs.Size.X / 2.0f) { diffRight.X = 0.0f; }
if (diffRight.X >= 0)
{
float distFallOffRight = diffRight.Length() / FireSoundRange;
if (distFallOffRight < 0.99f)
{
fireVolumeRight[0] += 1.0f - distFallOffRight;
if (fs.Size.X > FireSoundLargeLimit) fireVolumeRight[1] += (1.0f - distFallOffRight) * ((fs.Size.X - FireSoundLargeLimit) / FireSoundLargeLimit);
}
}
}
AddFireVolume(fs);
}
foreach (FireSource fs in hull.FakeFireSources)
{
AddFireVolume(fs);
}
}
@@ -579,13 +615,58 @@ namespace Barotrauma
Vector2 soundPos = new Vector2(GameMain.SoundManager.ListenerPosition.X + (fireVolumeRight[i] - fireVolumeLeft[i]) * 100, GameMain.SoundManager.ListenerPosition.Y);
if (fireSoundChannels[i] == null || !fireSoundChannels[i].IsPlaying)
{
fireSoundChannels[i] = GetSound(i == 0 ? "fire" : "firelarge").Play(1.0f, FlowSoundRange, soundPos);
fireSoundChannels[i] = GetSound(fireSoundTags[i]).Play(1.0f, FlowSoundRange, soundPos);
fireSoundChannels[i].Looping = true;
}
fireSoundChannels[i].Gain = Math.Max(fireVolumeRight[i], fireVolumeLeft[i]);
fireSoundChannels[i].Position = new Vector3(soundPos, 0.0f);
}
}
void AddFireVolume(FireSource fs)
{
Vector2 diff = fs.WorldPosition + fs.Size / 2 - listenerPos;
if (Math.Abs(diff.X) < FireSoundRange && Math.Abs(diff.Y) < FireSoundRange)
{
Vector2 diffLeft = (fs.WorldPosition + new Vector2(fs.Size.X, fs.Size.Y / 2)) - listenerPos;
if (Math.Abs(diff.X) < fs.Size.X / 2.0f) { diffLeft.X = 0.0f; }
if (diffLeft.X <= 0)
{
float distFallOffLeft = diffLeft.Length() / FireSoundRange;
if (distFallOffLeft < 0.99f)
{
fireVolumeLeft[0] += (1.0f - distFallOffLeft);
if (fs.Size.X > FireSoundLargeLimit)
{
fireVolumeLeft[2] += (1.0f - distFallOffLeft) * ((fs.Size.X - FireSoundLargeLimit) / FireSoundLargeLimit);
}
else if (fs.Size.X > FireSoundMediumLimit)
{
fireVolumeLeft[1] += (1.0f - distFallOffLeft) * ((fs.Size.X - FireSoundMediumLimit) / FireSoundMediumLimit);
}
}
}
Vector2 diffRight = (fs.WorldPosition + new Vector2(0.0f, fs.Size.Y / 2)) - listenerPos;
if (Math.Abs(diff.X) < fs.Size.X / 2.0f) { diffRight.X = 0.0f; }
if (diffRight.X >= 0)
{
float distFallOffRight = diffRight.Length() / FireSoundRange;
if (distFallOffRight < 0.99f)
{
fireVolumeRight[0] += 1.0f - distFallOffRight;
if (fs.Size.X > FireSoundLargeLimit)
{
fireVolumeRight[2] += (1.0f - distFallOffRight) * ((fs.Size.X - FireSoundLargeLimit) / FireSoundLargeLimit);
}
else if (fs.Size.X > FireSoundMediumLimit)
{
fireVolumeRight[1] += (1.0f - distFallOffRight) * ((fs.Size.X - FireSoundMediumLimit) / FireSoundMediumLimit);
}
}
}
}
}
}
private static void UpdateRandomAmbience(float deltaTime)
@@ -606,6 +687,40 @@ namespace Barotrauma
}
}
private static void UpdateHullSounds(float deltaTime)
{
if (hullSoundChannel != null && hullSoundChannel.IsPlaying && hullSoundSource != null)
{
hullSoundChannel.Position = new Vector3(hullSoundSource.WorldPosition, 0.0f);
hullSoundChannel.Gain = GetHullSoundVolume(hullSoundSource.Submarine);
}
if (hullSoundTimer > 0.0f)
{
hullSoundTimer -= deltaTime;
}
else
{
if (!Level.IsLoadedOutpost && Character.Controlled?.CurrentHull?.Submarine is Submarine sub &&
sub.Info != null && !sub.Info.IsOutpost)
{
hullSoundSource = Character.Controlled.CurrentHull;
hullSoundChannel = PlaySound("hull", hullSoundSource.WorldPosition, volume: GetHullSoundVolume(sub), range: 1500.0f);
hullSoundTimer = Rand.Range(hullSoundInterval.X, hullSoundInterval.Y);
}
else
{
hullSoundTimer = 5.0f;
}
}
static float GetHullSoundVolume(Submarine sub)
{
var depth = Level.Loaded == null ? 0.0f : Math.Abs(sub.Position.Y - Level.Loaded.Size.Y) * Physics.DisplayToRealWorldRatio;
return Math.Clamp((depth - 800.0f) / 1500.0f, 0.4f, 1.0f);
}
}
public static Sound GetSound(string soundTag)
{
var matchingSounds = miscSounds[soundTag].ToList();
@@ -637,8 +752,8 @@ namespace Barotrauma
{
if (sound == null)
{
string errorMsg = "Error in SoundPlayer.PlaySound (sound was null)\n" + Environment.StackTrace;
GameAnalyticsManager.AddErrorEventOnce("SoundPlayer.PlaySound:SoundNull" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
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);
return null;
}
@@ -830,6 +945,18 @@ namespace Barotrauma
{
return "wreck";
}
if (Level.IsLoadedOutpost && Character.Controlled.Submarine == Level.Loaded.StartOutpost)
{
// Only return music type for specific outpost types to not assume that
// every outpost type has an associated music track (switch-case for future tracks)
var locationType = Level.Loaded.StartLocation?.Type?.Identifier?.ToLowerInvariant();
switch (locationType)
{
case "research":
return locationType;
}
}
}
Submarine targetSubmarine = Character.Controlled?.Submarine;
@@ -952,6 +1079,16 @@ namespace Barotrauma
}
}
tempList.GetRandom().sound?.Play(1.0f, range, position, muffle: ShouldMuffleSound(Character.Controlled, position, range, null));
}
}
public static void PlayUISound(GUISoundType soundType)
{
if (guiSounds == null || guiSounds.Count < 1) { return; }
if (guiSounds.TryGetValue(soundType, out List<Sound> sounds))
{
if (sounds == null || sounds.Count < 1) { return; }
sounds.GetRandom()?.Play(null, "ui");
}
}
}
}
@@ -67,8 +67,8 @@ namespace Barotrauma
{
if (sound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace;
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
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);
return;
}
soundChannel = SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, hullGuess: hull);
@@ -93,10 +93,14 @@ namespace Barotrauma
var selectedSound = sounds[selectedSoundIndex];
if (selectedSound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace;
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
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);
return;
}
if (selectedSound.Sound.Disposed)
{
Submarine.ReloadRoundSound(selectedSound);
}
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, worldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: hull);
if (soundChannel != null) { soundChannel.Looping = loopSound; }
}
@@ -0,0 +1,559 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
internal readonly struct InventorySlotItem
{
public readonly int Slot;
public readonly Item Item;
public InventorySlotItem(int slot, Item item)
{
Slot = slot;
Item = item;
}
public void Deconstruct(out int slot, out Item item)
{
slot = Slot;
item = Item;
}
}
internal abstract partial class Command
{
public abstract string GetDescription();
}
/// <summary>
/// A command for setting and reverting a MapEntity rectangle
/// <see cref="SubEditorScreen"/>
/// <see cref="MapEntity"/>
/// </summary>
internal class TransformCommand : Command
{
private readonly List<MapEntity> Receivers;
private readonly List<Rectangle> NewData;
private readonly List<Rectangle> OldData;
private readonly bool Resized;
/// <summary>
/// A command for setting and reverting a MapEntity rectangle
/// </summary>
/// <param name="receivers">Entities whose rectangle has been altered</param>
/// <param name="newData">The new rectangle that is or will be applied to the map entity</param>
/// <param name="oldData">Old rectangle the map entity had before</param>
/// <param name="resized">If the transform was resized or not</param>
/// <remarks>
/// All lists should be equal in length, for every receiver there should be a corresponding entry at the same position in newData and oldData.
/// </remarks>
public TransformCommand(List<MapEntity> receivers, List<Rectangle> newData, List<Rectangle> oldData, bool resized)
{
Receivers = receivers;
NewData = newData;
OldData = oldData;
Resized = resized;
}
public override void Execute() => SetRects(NewData);
public override void UnExecute() => SetRects(OldData);
public override void Cleanup()
{
NewData.Clear();
OldData.Clear();
Receivers.Clear();
}
private void SetRects(IReadOnlyList<Rectangle> rects)
{
if (Receivers.Count != rects.Count)
{
DebugConsole.ThrowError($"Receivers.Count did not match Rects.Count ({Receivers.Count} vs {rects.Count}).");
return;
}
for (int i = 0; i < rects.Count; i++)
{
MapEntity entity = Receivers[i].GetReplacementOrThis();
Rectangle Rect = rects[i];
Vector2 diff = Rect.Location.ToVector2() - entity.Rect.Location.ToVector2();
entity.Move(diff);
entity.Rect = Rect;
}
}
public override string GetDescription()
{
if (Resized)
{
return TextManager.GetWithVariable("Undo.ResizedItem", "[item]", Receivers.FirstOrDefault()?.Name);
}
return Receivers.Count > 1
? TextManager.GetWithVariable("Undo.MovedItemsMultiple", "[count]", Receivers.Count.ToString())
: TextManager.GetWithVariable("Undo.MovedItem", "[item]", Receivers.FirstOrDefault()?.Name);
}
}
/// <summary>
/// A command that removes and unremoves map entities
/// <see cref="ItemPrefab"/>
/// <see cref="StructurePrefab"/>
/// <seealso cref="SubEditorScreen"/>
/// </summary>
internal class AddOrDeleteCommand : Command
{
private readonly Dictionary<InventorySlotItem, Inventory> PreviousInventories = new Dictionary<InventorySlotItem, Inventory>();
private readonly List<MapEntity> Receivers;
private readonly List<MapEntity> CloneList;
private readonly bool WasDeleted;
private readonly List<AddOrDeleteCommand> ContainedItemsCommand = new List<AddOrDeleteCommand>();
/// <summary>
/// Creates a command where all entities share the same state.
/// </summary>
/// <param name="receivers">Entities that were deleted or added</param>
/// <param name="wasDeleted">Whether or not all entities are or are going to be deleted</param>
public AddOrDeleteCommand(List<MapEntity> receivers, bool wasDeleted)
{
WasDeleted = wasDeleted;
Receivers = receivers;
try
{
foreach (MapEntity receiver in receivers)
{
if (receiver is Item it && it.ParentInventory != null)
{
PreviousInventories.Add(new InventorySlotItem(Array.IndexOf(it.ParentInventory.Items, it), it), it.ParentInventory);
}
}
List<MapEntity> clonedTargets = MapEntity.Clone(receivers);
List<MapEntity> itemsToDelete = new List<MapEntity>();
foreach (MapEntity receiver in Receivers)
{
if (receiver is Item it)
{
foreach (ItemContainer component in it.GetComponents<ItemContainer>())
{
if (component.Inventory == null) { continue; }
itemsToDelete.AddRange(component.Inventory.Items.Where(item => item != null && !item.Removed));
}
}
}
if (itemsToDelete.Any())
{
ContainedItemsCommand.Add(new AddOrDeleteCommand(itemsToDelete, true));
foreach (MapEntity item in itemsToDelete)
{
if (item != null && !item.Removed)
{
item.Remove();
}
}
}
foreach (MapEntity clone in clonedTargets)
{
clone.ShallowRemove();
if (clone is Item it)
{
foreach (ItemContainer container in it.GetComponents<ItemContainer>())
{
container.Inventory?.DeleteAllItems();
}
}
}
CloneList = clonedTargets;
}
// This should never happen except if we decide to make a new type of MapEntity that isn't finished yet
catch (Exception e)
{
Receivers = new List<MapEntity>();
CloneList = new List<MapEntity>();
DebugConsole.ThrowError("Could not store object", e);
}
}
public override void Execute()
{
DeleteUndelete(true);
ContainedItemsCommand?.ForEach(cmd => cmd.Execute());
}
public override void UnExecute()
{
DeleteUndelete(false);
ContainedItemsCommand?.ForEach(cmd => cmd.UnExecute());
}
public override void Cleanup()
{
foreach (MapEntity entity in CloneList)
{
if (!entity.Removed)
{
entity.Remove();
}
}
CloneList?.Clear();
Receivers.Clear();
PreviousInventories?.Clear();
ContainedItemsCommand?.ForEach(cmd => cmd.Cleanup());
}
private void DeleteUndelete(bool redo)
{
bool wasDeleted = WasDeleted;
// We are redoing instead of undoing, flip the behavior
if (redo) { wasDeleted = !wasDeleted; }
if (wasDeleted)
{
Debug.Assert(Receivers.All(entity => entity.GetReplacementOrThis().Removed), "Tried to redo a deletion but some items were not deleted");
List<MapEntity> clones = MapEntity.Clone(CloneList);
for (int i = 0; i < Math.Min(Receivers.Count, clones.Count); i++)
{
MapEntity clone = clones[i];
MapEntity receiver = Receivers[i];
if (receiver.GetReplacementOrThis() is Item item && clone is Item cloneItem)
{
foreach (ItemComponent ic in item.Components)
{
int index = item.GetComponentIndex(ic);
ItemComponent component = cloneItem.Components.ElementAtOrDefault(index);
switch (component)
{
case null:
continue;
case ItemContainer newContainer when newContainer.Inventory != null && ic is ItemContainer itemContainer && itemContainer.Inventory != null:
itemContainer.Inventory.GetReplacementOrThiS().ReplacedBy = newContainer.Inventory;
goto default;
default:
ic.GetReplacementOrThis().ReplacedBy = component;
break;
}
}
}
receiver.GetReplacementOrThis().ReplacedBy = clone;
if (clone is Item it)
{
foreach (var (slotRef, inventory) in PreviousInventories)
{
if (slotRef.Item == receiver)
{
inventory.GetReplacementOrThiS().TryPutItem(it, slotRef.Slot, false, false, null, createNetworkEvent: false);
}
}
}
}
foreach (MapEntity clone in clones)
{
clone.Submarine = Submarine.MainSub;
}
}
else
{
foreach (MapEntity t in Receivers)
{
MapEntity receiver = t.GetReplacementOrThis();
if (!receiver.Removed)
{
receiver.Remove();
}
}
}
}
public void MergeInto(AddOrDeleteCommand master)
{
master.Receivers.AddRange(Receivers);
master.CloneList.AddRange(CloneList);
master.ContainedItemsCommand.AddRange(ContainedItemsCommand);
foreach (var (slot, item) in PreviousInventories)
{
master.PreviousInventories.Add(slot, item);
}
}
public override string GetDescription()
{
if (WasDeleted)
{
return Receivers.Count > 1
? TextManager.GetWithVariable("Undo.RemovedItemsMultiple", "[count]", Receivers.Count.ToString())
: TextManager.GetWithVariable("Undo.RemovedItem", "[item]", Receivers.FirstOrDefault()?.Name);
}
return Receivers.Count > 1
? TextManager.GetWithVariable("Undo.AddedItemsMultiple", "[count]", Receivers.Count.ToString())
: TextManager.GetWithVariable("Undo.AddedItem", "[item]", Receivers.FirstOrDefault()?.Name);
}
}
/// <summary>
/// A command that places or drops items out of inventories
/// </summary>
/// <see cref="Inventory"/>
/// <see cref="MapEntity"/>
internal class InventoryPlaceCommand : Command
{
private readonly Inventory Inventory;
private readonly List<InventorySlotItem> Receivers;
private readonly bool wasDropped;
public InventoryPlaceCommand(Inventory inventory, List<Item> items, bool dropped)
{
Inventory = inventory;
Receivers = items.Select(item => new InventorySlotItem(Array.IndexOf(inventory.Items, item), item)).ToList();
wasDropped = dropped;
}
public override void Execute() => ContainUncontain(false);
public override void UnExecute() => ContainUncontain(true);
public override void Cleanup()
{
Receivers.Clear();
}
private void ContainUncontain(bool drop)
{
// flip the behavior if the item was dropped instead of inserted
if (wasDropped) { drop = !drop; }
foreach (var (slot, receiver) in Receivers)
{
Item item = (Item) receiver.GetReplacementOrThis();
if (drop)
{
item.Drop(null, createNetworkEvent: false);
}
else
{
Inventory.GetReplacementOrThiS().TryPutItem(item, slot, false, false, null, createNetworkEvent: false);
}
}
}
public override string GetDescription()
{
if (wasDropped)
{
return TextManager.GetWithVariable("Undo.DroppedItem", "[item]", Receivers.FirstOrDefault().Item.Name);
}
string container = "[ERROR]";
if (Inventory.Owner is Item item)
{
container = item.Name;
}
return Receivers.Count > 1
? TextManager.GetWithVariables("Undo.ContainedItemsMultiple", new[] { "[count]", "[container]" }, new[] { Receivers.Count.ToString(), container })
: TextManager.GetWithVariables("Undo.ContainedItem", new[] { "[item]", "[container]" }, new[] { Receivers.FirstOrDefault().Item.Name, container });
}
}
/// <summary>
/// A command that sets item properties
/// </summary>
internal class PropertyCommand : Command
{
private Dictionary<object, List<ISerializableEntity>> OldProperties;
private readonly List<ISerializableEntity> Receivers;
private readonly string PropertyName;
private readonly object NewProperties;
private string sanitizedProperty;
public readonly int PropertyCount;
/// <summary>
/// A command that sets item properties
/// </summary>
/// <param name="receivers">Affected entities</param>
/// <param name="propertyName">Real property name, not all lowercase</param>
/// <param name="newData"></param>
/// <param name="oldData"></param>
public PropertyCommand(List<ISerializableEntity> receivers, string propertyName, object newData, Dictionary<object, List<ISerializableEntity>> oldData)
{
Receivers = receivers;
PropertyName = propertyName;
OldProperties = oldData;
NewProperties = newData;
PropertyCount = receivers.Count;
SanitizeProperty();
}
public PropertyCommand(ISerializableEntity receiver, string propertyName, object newData, object oldData)
{
Receivers = new List<ISerializableEntity> { receiver };
PropertyName = propertyName;
OldProperties = new Dictionary<object, List<ISerializableEntity>> { { oldData, Receivers } };
NewProperties = newData;
PropertyCount = 1;
SanitizeProperty();
}
public bool MergeInto(PropertyCommand master)
{
if (!master.Receivers.SequenceEqual(Receivers)) { return false; }
master.OldProperties = OldProperties;
return true;
}
private void SanitizeProperty()
{
sanitizedProperty = NewProperties switch
{
float f => f.FormatSingleDecimal(),
Point point => XMLExtensions.PointToString(point),
Vector2 vector2 => vector2.FormatZeroDecimal(),
Vector3 vector3 => vector3.FormatSingleDecimal(),
Vector4 vector4 => vector4.FormatSingleDecimal(),
Color color => XMLExtensions.ColorToString(color),
Rectangle rectangle => XMLExtensions.RectToString(rectangle),
_ => NewProperties.ToString()
};
}
public override void Execute() => SetProperties(false);
public override void UnExecute() => SetProperties(true);
public override void Cleanup()
{
Receivers.Clear();
OldProperties.Clear();
}
private void SetProperties(bool undo)
{
foreach (ISerializableEntity t in Receivers)
{
ISerializableEntity receiver;
switch (t)
{
case MapEntity me when me.GetReplacementOrThis() is ISerializableEntity sEntity:
receiver = sEntity;
break;
case ItemComponent ic when ic.GetReplacementOrThis() is ISerializableEntity sItemComponent:
receiver = sItemComponent;
break;
default:
receiver = t;
break;
}
object data = NewProperties;
if (undo)
{
foreach (var (key, value) in OldProperties)
{
if (value.Contains(t)) { data = key; }
}
}
if (receiver.SerializableProperties != null)
{
Dictionary<string, SerializableProperty> props = receiver.SerializableProperties;
if (props.TryGetValue(PropertyName.ToLowerInvariant(), out SerializableProperty prop))
{
prop.TrySetValue(receiver, data);
// Update the editing hud
if (MapEntity.EditingHUD == null || (MapEntity.EditingHUD.UserData != receiver && (receiver is ItemComponent ic && MapEntity.EditingHUD.UserData != ic.Item))) { continue; }
GUIListBox list = MapEntity.EditingHUD.GetChild<GUIListBox>();
if (list == null) { continue; }
IEnumerable<SerializableEntityEditor> editors = list.Content.FindChildren(comp => comp is SerializableEntityEditor).Cast<SerializableEntityEditor>();
SerializableEntityEditor.LockEditing = true;
foreach (SerializableEntityEditor editor in editors)
{
if (editor.UserData == receiver && editor.Fields.TryGetValue(PropertyName, out GUIComponent[] _))
{
editor.UpdateValue(prop, data);
}
}
SerializableEntityEditor.LockEditing = false;
}
}
}
}
public override string GetDescription()
{
return Receivers.Count > 1
? TextManager.GetWithVariables("Undo.ChangedPropertyMultiple", new[] { "[property]", "[count]", "[value]" }, new[] { PropertyName, Receivers.Count.ToString(), sanitizedProperty })
: TextManager.GetWithVariables("Undo.ChangedProperty", new[] { "[property]", "[item]", "[value]" }, new[] { PropertyName, Receivers.FirstOrDefault()?.Name, sanitizedProperty });
}
}
/// <summary>
/// A command that moves items around in inventories
/// </summary>
/// <see cref="oldInventory"/>
/// <see cref="MapEntity"/>
internal class InventoryMoveCommand : Command
{
private readonly Inventory oldInventory;
private readonly Inventory newInventory;
private readonly int oldSlot;
private readonly int newSlot;
private readonly Item targetItem;
public InventoryMoveCommand(Inventory oldInventory, Inventory newInventory, Item item, int oldSlot, int newSlot)
{
this.newInventory = newInventory;
this.oldInventory = oldInventory;
this.oldSlot = oldSlot;
this.newSlot = newSlot;
targetItem = item;
}
public override void Execute()
{
if (targetItem.GetReplacementOrThis() is Item item)
{
newInventory?.GetReplacementOrThiS().TryPutItem(item, newSlot, true, false, null, createNetworkEvent: false);
}
}
public override void UnExecute()
{
if (targetItem.GetReplacementOrThis() is Item item)
{
oldInventory?.GetReplacementOrThiS().TryPutItem(item, oldSlot, true, false, null, createNetworkEvent: false);
}
}
public override void Cleanup() { }
public override string GetDescription()
{
return TextManager.GetWithVariable("Undo.MovedItem", "[item]", targetItem.Name);
}
}
}
@@ -142,10 +142,10 @@ namespace Barotrauma
if (gradient.Length == 0)
{
#if DEBUG
DebugConsole.ThrowError("Empty color array passed to the GradientLerp method.\n" + Environment.StackTrace);
DebugConsole.ThrowError("Empty color array passed to the GradientLerp method.\n" + Environment.StackTrace.CleanupStackTrace());
#endif
GameAnalyticsManager.AddErrorEventOnce("ToolBox.GradientLerp:EmptyColorArray", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Empty color array passed to the GradientLerp method.\n" + Environment.StackTrace);
"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.10.5.1</Version>
<Version>0.10.600.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -178,6 +178,10 @@
<_Parameter1>GitBranch</_Parameter1>
<_Parameter2>$(BuildBranch)</_Parameter2>
</AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>ProjectDir</_Parameter1>
<_Parameter2>$(ProjectDir)</_Parameter2>
</AssemblyAttributes>
</ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file -->
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
+5 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.10.5.1</Version>
<Version>0.10.600.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -180,6 +180,10 @@
<_Parameter1>GitBranch</_Parameter1>
<_Parameter2>$(BuildBranch)</_Parameter2>
</AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>ProjectDir</_Parameter1>
<_Parameter2>$(ProjectDir)</_Parameter2>
</AssemblyAttributes>
</ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file -->
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.10.5.1</Version>
<Version>0.10.600.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -209,6 +209,10 @@
<_Parameter1>GitBranch</_Parameter1>
<_Parameter2>$(BuildBranch)</_Parameter2>
</AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>ProjectDir</_Parameter1>
<_Parameter2>$(ProjectDir)</_Parameter2>
</AssemblyAttributes>
</ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file -->
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.10.5.1</Version>
<Version>0.10.600.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>
@@ -127,6 +127,10 @@
<_Parameter1>GitBranch</_Parameter1>
<_Parameter2>$(BuildBranch)</_Parameter2>
</AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>ProjectDir</_Parameter1>
<_Parameter2>$(ProjectDir)</_Parameter2>
</AssemblyAttributes>
</ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file -->
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
+5 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.10.5.1</Version>
<Version>0.10.600.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>
@@ -140,6 +140,10 @@
<_Parameter1>GitBranch</_Parameter1>
<_Parameter2>$(BuildBranch)</_Parameter2>
</AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>ProjectDir</_Parameter1>
<_Parameter2>$(ProjectDir)</_Parameter2>
</AssemblyAttributes>
</ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file -->
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
@@ -528,19 +528,19 @@ namespace Barotrauma
{
msg.Write(true);
msg.Write((byte)Order.PrefabList.IndexOf(info.CurrentOrder.Prefab));
msg.Write(info.CurrentOrder.TargetEntity == null ? (UInt16)0 :
info.CurrentOrder.TargetEntity.ID);
if (info.CurrentOrder.OrderGiver != null)
msg.Write(info.CurrentOrder.TargetEntity == null ? (UInt16)0 : info.CurrentOrder.TargetEntity.ID);
var hasOrderGiver = info.CurrentOrder.OrderGiver != null;
msg.Write(hasOrderGiver);
if (hasOrderGiver) { msg.Write(info.CurrentOrder.OrderGiver.ID); }
msg.Write((byte)(string.IsNullOrWhiteSpace(info.CurrentOrderOption) ? 0 : Array.IndexOf(info.CurrentOrder.Prefab.Options, info.CurrentOrderOption)));
var hasTargetPosition = info.CurrentOrder.TargetPosition != null;
msg.Write(hasTargetPosition);
if (hasTargetPosition)
{
msg.Write(true);
msg.Write(info.CurrentOrder.OrderGiver.ID);
msg.Write(info.CurrentOrder.TargetPosition.Position.X);
msg.Write(info.CurrentOrder.TargetPosition.Position.Y);
msg.Write(info.CurrentOrder.TargetPosition.Hull == null ? (UInt16)0 : info.CurrentOrder.TargetPosition.Hull.ID);
}
else
{
msg.Write(false);
}
msg.Write((byte)(string.IsNullOrWhiteSpace(info.CurrentOrderOption) ? 0 :
Array.IndexOf(info.CurrentOrder.Prefab.Options, info.CurrentOrderOption)));
}
else
{
@@ -247,7 +247,7 @@ namespace Barotrauma
catch (Exception e)
{
string errorMsg = "Failed to write input to command line (window width: " + Console.WindowWidth + ", window height: " + Console.WindowHeight + ")\n"
+ e.Message + "\n" + e.StackTrace;
+ e.Message + "\n" + e.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("DebugConsole.RewriteInputToCommandLine", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
}
@@ -1352,7 +1352,7 @@ namespace Barotrauma
ServerEntityEvent ev = GameMain.Server.EntityEventManager.Events[Convert.ToUInt16(args[0])];
if (ev != null)
{
NewMessage(ev.StackTrace, Color.Lime);
NewMessage(ev.StackTrace.CleanupStackTrace(), Color.Lime);
}
}));
@@ -694,6 +694,10 @@ namespace Barotrauma
}
pendingHireInfos.Add(match);
if (pendingHireInfos.Count + CrewManager.CharacterInfos.Count() >= CrewManager.MaxCrewSize)
{
break;
}
}
location.HireManager.PendingHires = pendingHireInfos;
}
@@ -67,8 +67,26 @@ namespace Barotrauma.Items.Components
if (!CheckCharacterSuccess(c.Character))
{
item.CreateServerEvent(this);
c.Character.SelectedItems[0]?.GetComponent<Wire>()?.CreateNetworkEvent();
c.Character.SelectedItems[1]?.GetComponent<Wire>()?.CreateNetworkEvent();
c.Character.Inventory?.CreateNetworkEvent();
for (int i = 0; i < 2; i++)
{
var selectedWire = c.Character.SelectedItems[i]?.GetComponent<Wire>();
if (selectedWire == null) { continue; }
selectedWire.CreateNetworkEvent();
var panel1 = selectedWire.Connections[0]?.ConnectionPanel;
if (panel1 != null && panel1 != this) { panel1.item.CreateServerEvent(panel1); }
var panel2 = selectedWire.Connections[1]?.ConnectionPanel;
if (panel2 != null && panel2 != this) { panel2.item.CreateServerEvent(panel2); }
CoroutineManager.InvokeAfter(() =>
{
item.CreateServerEvent(this);
if (panel1 != null && panel1 != this) { panel1.item.CreateServerEvent(panel1); }
if (panel2 != null && panel2 != this) { panel2.item.CreateServerEvent(panel2); }
if (!selectedWire.Item.Removed) { selectedWire.CreateNetworkEvent(); }
}, 1.0f);
}
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, this, c.Character.ID });
return;
}
@@ -371,7 +371,7 @@ namespace Barotrauma
if (!ItemList.Contains(this))
{
string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet.\n" + Environment.StackTrace;
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);
return;
@@ -12,7 +12,7 @@ namespace Barotrauma
private float lastSentVolume, lastSentOxygen, lastSentFireCount;
private float sendUpdateTimer;
private bool decalsChanged;
private bool decalUpdatePending;
public override bool IsMouseOn(Vector2 position)
{
@@ -32,27 +32,21 @@ namespace Barotrauma
return;
}
if (decalsChanged)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { false });
lastSentVolume = waterVolume;
lastSentOxygen = OxygenPercentage;
lastSentFireCount = FireSources.Count;
sendUpdateTimer = NetConfig.HullUpdateInterval;
decalsChanged = false;
return;
}
sendUpdateTimer -= deltaTime;
//update client hulls if the amount of water has changed by >10%
//or if oxygen percentage has changed by 5%
if (Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f || Math.Abs(lastSentOxygen - OxygenPercentage) > 5f ||
lastSentFireCount != FireSources.Count || FireSources.Count > 0 ||
pendingSectionUpdates.Count > 0 ||
sendUpdateTimer < -NetConfig.SparseHullUpdateInterval)
sendUpdateTimer < -NetConfig.SparseHullUpdateInterval ||
decalUpdatePending)
{
if (sendUpdateTimer < 0.0f)
{
if (decalUpdatePending)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { false });
}
if (pendingSectionUpdates.Count > 0)
{
foreach (int pendingSectionUpdate in pendingSectionUpdates)
@@ -120,8 +114,9 @@ namespace Barotrauma
foreach (Decal decal in decals)
{
message.Write(decal.Prefab.UIntIdentifier);
float normalizedXPos = MathHelper.Clamp(MathUtils.InverseLerp(rect.X, rect.Right, decal.Position.X), 0.0f, 1.0f);
float normalizedYPos = MathHelper.Clamp(MathUtils.InverseLerp(rect.Y - rect.Height, rect.Y, decal.Position.Y), 0.0f, 1.0f);
message.Write((byte)decal.SpriteIndex);
float normalizedXPos = MathHelper.Clamp(MathUtils.InverseLerp(0.0f, rect.Width, decal.CenterPosition.X), 0.0f, 1.0f);
float normalizedYPos = MathHelper.Clamp(MathUtils.InverseLerp(-rect.Height, 0.0f, decal.CenterPosition.Y), 0.0f, 1.0f);
message.WriteRangedSingle(normalizedXPos, 0.0f, 1.0f, 8);
message.WriteRangedSingle(normalizedYPos, 0.0f, 1.0f, 8);
message.WriteRangedSingle(decal.Scale, 0f, 2f, 12);
@@ -332,7 +332,7 @@ namespace Barotrauma.Networking
catch (Exception e)
{
string errorMsg = "Error while writing banlist. {" + e + "}\n" + e.StackTrace;
string errorMsg = "Error while writing banlist. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("Banlist.ServerAdminWrite", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
throw;
}
@@ -1,7 +1,5 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Text;
namespace Barotrauma.Networking
@@ -14,19 +12,25 @@ namespace Barotrauma.Networking
UInt16 ID = msg.ReadUInt16();
ChatMessageType type = (ChatMessageType)msg.ReadByte();
string txt = "";
string txt;
int orderIndex = -1;
Character orderTargetCharacter = null;
Entity orderTargetEntity = null;
int orderOptionIndex = -1;
OrderChatMessage orderMsg = null;
OrderTarget orderTargetPosition = null;
if (type == ChatMessageType.Order)
{
orderIndex = msg.ReadByte();
int orderIndex = msg.ReadByte();
orderTargetCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
orderTargetEntity = Entity.FindEntityByID(msg.ReadUInt16()) as Entity;
orderOptionIndex = msg.ReadByte();
int orderOptionIndex = msg.ReadByte();
if (msg.ReadBoolean())
{
var x = msg.ReadSingle();
var y = msg.ReadSingle();
var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull;
orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, true);
}
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
{
@@ -37,7 +41,7 @@ namespace Barotrauma.Networking
Order order = Order.PrefabList[orderIndex];
string orderOption = orderOptionIndex < 0 || orderOptionIndex >= order.Options.Length ? "" : order.Options[orderOptionIndex];
orderMsg = new OrderChatMessage(order, orderOption, orderTargetEntity, orderTargetCharacter, c.Character);
orderMsg = new OrderChatMessage(order, orderOption, orderTargetPosition ?? orderTargetEntity as ISpatialEntity, orderTargetCharacter, c.Character);
txt = orderMsg.Text;
}
else
@@ -114,20 +118,14 @@ namespace Barotrauma.Networking
if (type == ChatMessageType.Order)
{
if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead) return;
ChatMessageType messageType = CanUseRadio(orderMsg.Sender) ? ChatMessageType.Radio : ChatMessageType.Default;
if (orderMsg.Order.TargetAllCharacters)
if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead) { return; }
if (!orderMsg.Order.TargetAllCharacters && orderTargetCharacter != null)
{
//do nothing
var order = orderTargetPosition == null ?
new Order(orderMsg.Order.Prefab, orderTargetEntity, orderMsg.Order.Prefab?.GetTargetItemComponent(orderTargetEntity as Item), orderMsg.Sender) :
new Order(orderMsg.Order.Prefab, orderTargetPosition, orderMsg.Sender);
orderTargetCharacter.SetOrder(order, orderMsg.OrderOption, orderMsg.Sender);
}
else if (orderTargetCharacter != null)
{
orderTargetCharacter.SetOrder(
new Order(orderMsg.Order.Prefab, orderTargetEntity, (orderTargetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == orderMsg.Order.ItemComponentType)),
orderMsg.OrderOption, orderMsg.Sender);
}
GameMain.Server.SendOrderChatMessage(orderMsg);
}
else
@@ -272,7 +272,7 @@ namespace Barotrauma.Networking
GameAnalyticsManager.AddErrorEventOnce(
"FileSender.Update:Exception",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"FileSender threw an exception when trying to send data:\n" + e.Message + "\n" + e.StackTrace);
"FileSender threw an exception when trying to send data:\n" + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
transfer.Status = FileTransferStatus.Error;
return;
}
@@ -615,14 +615,14 @@ 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"
+ e.Message + "\n" + e.StackTrace;
+ e.Message + "\n" + e.StackTrace.CleanupStackTrace();
if (e.InnerException != null)
{
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace;
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace.CleanupStackTrace();
}
GameAnalyticsManager.AddErrorEventOnce(
"GameServer.Update:ClientWriteFailed" + e.StackTrace,
"GameServer.Update:ClientWriteFailed" + e.StackTrace.CleanupStackTrace(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMsg);
}
@@ -1796,7 +1796,7 @@ namespace Barotrauma.Networking
outmsg.Write(GameMain.NetLobbyScreen.SelectedShuttle.Name);
outmsg.Write(GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash.ToString());
string campaignSubmarineIndexes = string.Empty;
List<int> campaignSubIndices = new List<int>();
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
{
List<SubmarineInfo> subList = GameMain.NetLobbyScreen.GetSubList();
@@ -1804,17 +1804,15 @@ namespace Barotrauma.Networking
{
if (GameMain.NetLobbyScreen.CampaignSubmarines.Contains(subList[i]))
{
campaignSubmarineIndexes += i.ToString();
campaignSubmarineIndexes += ";";
campaignSubIndices.Add(i);
}
}
if (campaignSubmarineIndexes.Length > 0)
{
campaignSubmarineIndexes.Trim(';');
}
}
outmsg.Write(campaignSubmarineIndexes);
outmsg.Write((UInt16)campaignSubIndices.Count);
foreach (int campaignSubIndex in campaignSubIndices)
{
outmsg.Write((UInt16)campaignSubIndex);
}
outmsg.Write(serverSettings.Voting.AllowSubVoting);
outmsg.Write(serverSettings.Voting.AllowModeVoting);
@@ -2437,7 +2435,7 @@ namespace Barotrauma.Networking
if (GameSettings.VerboseLogging)
{
Log("Ending the round...\n" + Environment.StackTrace, ServerLog.MessageType.ServerMessage);
Log("Ending the round...\n" + Environment.StackTrace.CleanupStackTrace(), ServerLog.MessageType.ServerMessage);
}
else
@@ -2772,7 +2770,7 @@ namespace Barotrauma.Networking
{
if (recipient == null)
{
string errorMsg = "Attempted to send a chat message to a null client.\n" + Environment.StackTrace;
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);
return;
@@ -3069,12 +3067,13 @@ namespace Barotrauma.Networking
Client.UpdateKickVotes(connectedClients);
int minimumKickVotes = Math.Max(1, (int)(connectedClients.Count * serverSettings.KickVoteRequiredRatio));
var clientsToKick = connectedClients.FindAll(c =>
c.Connection != OwnerConnection &&
!c.HasPermission(ClientPermissions.Kick) &&
!c.HasPermission(ClientPermissions.Ban) &&
!c.HasPermission(ClientPermissions.Unban) &&
c.KickVoteCount >= connectedClients.Count * serverSettings.KickVoteRequiredRatio);
c.KickVoteCount >= minimumKickVotes);
foreach (Client c in clientsToKick)
{
var previousPlayer = previousPlayers.Find(p => p.MatchesClient(c));
@@ -32,7 +32,7 @@ namespace Barotrauma.Networking
createTime = Timing.TotalTime;
#if DEBUG
StackTrace = Environment.StackTrace.ToString();
StackTrace = Environment.StackTrace.CleanupStackTrace();
#endif
}
@@ -121,12 +121,12 @@ namespace Barotrauma.Networking
if (((Entity)entity).Removed && !(entity is Level))
{
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the entity has been removed.\n"+Environment.StackTrace);
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the entity has been removed.\n"+Environment.StackTrace.CleanupStackTrace());
return;
}
if (((Entity)entity).IdFreed)
{
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the ID of the entity has been freed.\n"+Environment.StackTrace);
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the ID of the entity has been freed.\n"+Environment.StackTrace.CleanupStackTrace());
return;
}
@@ -197,12 +197,12 @@ namespace Barotrauma.Networking
if (GameSettings.VerboseLogging)
{
string errorMsg = "Failed to read server event for entity \"" + entityName + "\"!";
GameServer.Log(errorMsg + "\n" + e.StackTrace, ServerLog.MessageType.Error);
GameServer.Log(errorMsg + "\n" + e.StackTrace.CleanupStackTrace(), ServerLog.MessageType.Error);
DebugConsole.ThrowError(errorMsg, e);
}
GameAnalyticsManager.AddErrorEventOnce("ServerEntityEventManager.Read:ReadFailed" + entityName,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to read server event for entity \"" + entityName + "\"!\n" + e.StackTrace);
"Failed to read server event for entity \"" + entityName + "\"!\n" + e.StackTrace.CleanupStackTrace());
}
bufferedEvent.IsProcessed = true;
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma.Networking
{
@@ -21,8 +19,19 @@ namespace Barotrauma.Networking
msg.Write((byte)Order.PrefabList.IndexOf(Order.Prefab));
msg.Write(TargetCharacter == null ? (UInt16)0 : TargetCharacter.ID);
msg.Write(TargetEntity == null ? (UInt16)0 : TargetEntity.ID);
msg.Write(TargetEntity is Entity ? (TargetEntity as Entity).ID : (UInt16)0);
msg.Write((byte)Array.IndexOf(Order.Prefab.Options, OrderOption));
if (TargetEntity is OrderTarget orderTarget)
{
msg.Write(true);
msg.Write(orderTarget.Position.X);
msg.Write(orderTarget.Position.Y);
msg.Write(orderTarget.Hull == null ? (UInt16)0 : orderTarget.Hull.ID);
}
else
{
msg.Write(false);
}
}
}
}
@@ -124,7 +124,7 @@ namespace Barotrauma.Networking
catch (Exception e)
{
string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace;
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);
#if DEBUG
DebugConsole.ThrowError(errorMsg);
@@ -87,7 +87,7 @@ namespace Barotrauma.Networking
if (!Client.IsValidName(name, serverSettings))
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid");
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "");
return;
}
@@ -101,7 +101,7 @@ namespace Barotrauma.Networking
catch (Exception e)
{
string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace;
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);
#if DEBUG
DebugConsole.ThrowError(errorMsg);
@@ -293,9 +293,9 @@ namespace Barotrauma.Networking
if (!started) { return; }
if (!(conn is SteamP2PConnection steamp2pConn)) { return; }
if (sendDisconnectMessage) { SendDisconnectMessage(steamp2pConn.SteamID, msg); }
if (connectedClients.Contains(steamp2pConn))
{
if (sendDisconnectMessage) SendDisconnectMessage(steamp2pConn.SteamID, msg);
steamp2pConn.Status = NetworkConnectionStatus.Disconnected;
connectedClients.Remove(steamp2pConn);
OnDisconnect?.Invoke(conn, msg);
@@ -41,7 +41,7 @@ namespace Barotrauma
setLinuxEnv();
#endif
Console.WriteLine("Barotrauma Dedicated Server " + GameMain.Version +
" (" + AssemblyInfo.GetBuildString() + ", branch " + AssemblyInfo.GetGitBranch() + ", revision " + AssemblyInfo.GetGitRevision() + ")");
" (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")");
string executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
Directory.SetCurrentDirectory(executableDir);
@@ -99,7 +99,7 @@ namespace Barotrauma
sb.AppendLine("\n");
sb.AppendLine("Barotrauma seems to have crashed. Sorry for the inconvenience! ");
sb.AppendLine("\n");
sb.AppendLine("Game version " + GameMain.Version + " (" + AssemblyInfo.GetBuildString() + ", branch " + AssemblyInfo.GetGitBranch() + ", revision " + AssemblyInfo.GetGitRevision() + ")");
sb.AppendLine("Game version " + GameMain.Version + " (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")");
if (GameMain.Config != null)
{
sb.AppendLine("Language: " + (GameMain.Config.Language ?? "none"));
@@ -124,7 +124,7 @@ namespace Barotrauma
sb.AppendLine("Exception: " + exception.Message + " (" + exception.GetType().ToString() + ")");
sb.AppendLine("Target site: " +exception.TargetSite.ToString());
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.StackTrace);
sb.AppendLine(exception.StackTrace.CleanupStackTrace());
sb.AppendLine("\n");
if (exception.InnerException != null)
@@ -135,7 +135,7 @@ namespace Barotrauma
sb.AppendLine("Target site: " + exception.InnerException.TargetSite.ToString());
}
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.InnerException.StackTrace);
sb.AppendLine(exception.InnerException.StackTrace.CleanupStackTrace());
}
sb.AppendLine("Last debug messages:");
@@ -261,7 +261,7 @@ namespace Barotrauma
if (GameMain.Server.ServerSettings.SubSelectionMode == SelectionMode.Random)
{
var nonShuttles = SubmarineInfo.SavedSubmarines.Where(c => !c.HasTag(SubmarineTag.Shuttle) && !c.HasTag(SubmarineTag.HideInMenus)).ToList();
var nonShuttles = SubmarineInfo.SavedSubmarines.Where(c => !c.HasTag(SubmarineTag.Shuttle) && !c.HasTag(SubmarineTag.HideInMenus) && c.IsPlayer).ToList();
SelectedSub = nonShuttles[Rand.Range(0, nonShuttles.Count)];
}
if (GameMain.Server.ServerSettings.ModeSelectionMode == SelectionMode.Random)
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.10.5.1</Version>
<Version>0.10.600.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>
@@ -135,6 +135,10 @@
<_Parameter1>GitBranch</_Parameter1>
<_Parameter2>$(BuildBranch)</_Parameter2>
</AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>ProjectDir</_Parameter1>
<_Parameter2>$(ProjectDir)</_Parameter2>
</AssemblyAttributes>
</ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file -->
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
@@ -80,17 +80,19 @@
<Character file="Content/Characters/Legacycrawler/Legacycrawler.xml" />
<Character file="Content/Characters/Legacymoloch/Legacymoloch.xml" />
<Character file="Content/Characters/Legacytigerthresher/Legacytigerthresher.xml" />
<Character file="Content/Characters/Legacywatcher/Legacywatcher.xml" />
<Character file="Content/Characters/Mudraptor/Mudraptor.xml" />
<Character file="Content/Characters/Mudraptor_unarmored/Mudraptor_unarmored.xml" />
<Character file="Content/Characters/Mantis/Mantis.xml" />
<Character file="Content/Characters/Moloch/Moloch.xml" />
<Character file="Content/Characters/Moloch_m/Moloch_m.xml" />
<Character file="Content/Characters/Molochblack/Molochblack.xml" />
<Character file="Content/Characters/Molochbaby/Molochbaby.xml" />
<Character file="Content/Characters/Watcher/Watcher.xml" />
<Character file="Content/Characters/Tigerthresher/Tigerthresher.xml" />
<Character file="Content/Characters/Bonethresher/Bonethresher.xml" />
<Character file="Content/Characters/Leucocyte/Leucocyte.xml" />
<Character file="Content/Characters/Terminalcell/Terminalcell.xml" />
<Character file="Content/Characters/Watcher/Watcher.xml" />
<Wreck file="Content/Map/Wrecks/Dugong_Wrecked.sub" />
<Wreck file="Content/Map/Wrecks/Kastrull_Wrecked.sub" />
<Wreck file="Content/Map/Wrecks/Berilia_Wrecked.sub" />
@@ -0,0 +1,32 @@
adolf
anal
anus
ass
bitch
blowjob
clitoris
cock
cunt
dick
dildo
dyke
gay
fag
faggot
fuck
hitler
homo
jew
kike
nazi
mudslime
nig
nigger
nigga
penis
pussy
queer
slut
twat
vagina
whore

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