v0.10.6.2

This commit is contained in:
Joonas Rikkonen
2020-10-29 17:55:26 +02:00
parent 20a69375ca
commit bbf06f0984
255 changed files with 6196 additions and 3096 deletions

View File

@@ -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;
}

View File

@@ -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)
@@ -568,20 +580,29 @@ namespace Barotrauma
soundTimer -= deltaTime;
}
else if (AIController != null)
{
{
switch (AIController.State)
{
case AIState.Attack:
PlaySound(CharacterSound.SoundType.Attack);
break;
default:
PlaySound(CharacterSound.SoundType.Idle);
var petBehavior = (AIController as EnemyAIController)?.PetBehavior;
if (petBehavior != null && petBehavior.Happiness < petBehavior.MaxHappiness * 0.25f)
{
PlaySound(CharacterSound.SoundType.Unhappy);
}
else
{
PlaySound(CharacterSound.SoundType.Idle);
}
break;
}
}
}
if (info != null || Vitality < MaxVitality * 0.98f)
if (info != null || Vitality < MaxVitality * 0.98f || IsPet)
{
hudInfoTimer -= deltaTime;
if (hudInfoTimer <= 0.0f)
@@ -653,7 +674,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 +686,7 @@ namespace Barotrauma
public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam)
{
if (!Enabled) { return; }
if (!Enabled || InvisibleTimer > 0.0f) { return; }
if (GameMain.DebugDraw)
{
@@ -760,49 +781,68 @@ namespace Barotrauma
MathHelper.Clamp(1.0f - (cursorDist - (hoverRange - fadeOutRange)) / fadeOutRange, 0.2f, 1.0f) :
1.0f;
if (!GUI.DisableCharacterNames && hudInfoVisible && info != null &&
(controlled == null || this != controlled.FocusedCharacter) && cam.Zoom > 0.4f)
if (!GUI.DisableCharacterNames && hudInfoVisible &&
(controlled == null || this != controlled.FocusedCharacter || IsPet) && cam.Zoom > 0.4f)
{
string name = Info.DisplayName;
if (controlled == null && name != Info.Name) { name += " " + TextManager.Get("Disguised"); }
Vector2 nameSize = GUI.Font.MeasureString(name);
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
namePos *= screenSize / viewportSize;
namePos.X = (float)Math.Floor(namePos.X); namePos.Y = (float)Math.Floor(namePos.Y);
namePos *= viewportSize / screenSize;
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
Color nameColor = Color.White;
if (Controlled != null && TeamID != Controlled.TeamID)
if (info != null)
{
nameColor = TeamID == TeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
string name = Info.DisplayName;
if (controlled == null && name != Info.Name) { name += " " + TextManager.Get("Disguised"); }
Vector2 nameSize = GUI.Font.MeasureString(name);
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
namePos *= screenSize / viewportSize;
namePos.X = (float)Math.Floor(namePos.X); namePos.Y = (float)Math.Floor(namePos.Y);
namePos *= viewportSize / screenSize;
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
Color nameColor = Color.White;
if (Controlled != null && TeamID != Controlled.TeamID)
{
nameColor = TeamID == TeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
}
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
{
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
if (iconStyle != null)
{
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.WorldPosition ?? WorldPosition + Vector2.UnitY * 100.0f;
Vector2 iconPos = headPos;
iconPos.Y = -iconPos.Y;
nameColor = iconStyle.Color;
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
}
}
GUI.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
GUI.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
if (GameMain.DebugDraw)
{
GUI.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
}
}
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
var petBehavior = (AIController as EnemyAIController)?.PetBehavior;
if (petBehavior != null && !IsDead && !IsUnconscious)
{
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
var petStatus = petBehavior.GetCurrentStatusIndicatorType();
var iconStyle = GUI.Style.GetComponentStyle("PetIcon." + petStatus);
if (iconStyle != null)
{
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.WorldPosition ?? WorldPosition + Vector2.UnitY * 100.0f;
Vector2 iconPos = headPos;
iconPos.Y = -iconPos.Y;
nameColor = iconStyle.Color;
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
}
}
GUI.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
GUI.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
if (GameMain.DebugDraw)
{
GUI.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
}
}
if (IsDead) { return; }
@@ -846,11 +886,12 @@ namespace Barotrauma
private readonly List<CharacterSound> matchingSounds = new List<CharacterSound>();
private SoundChannel soundChannel;
public void PlaySound(CharacterSound.SoundType soundType)
public void PlaySound(CharacterSound.SoundType soundType, float soundIntervalFactor = 1.0f)
{
if (sounds == null || sounds.Count == 0) { return; }
if (soundChannel != null && soundChannel.IsPlaying) { return; }
if (GameMain.SoundManager?.Disabled ?? true) { return; }
if (soundTimer > soundInterval * soundIntervalFactor) { return; }
matchingSounds.Clear();
foreach (var s in sounds)
{

View File

@@ -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;
@@ -321,6 +321,17 @@ namespace Barotrauma
{
character.SelectedConstruction.DrawHUD(spriteBatch, cam, Character.Controlled);
}
if (Character.Controlled.Inventory != null)
{
foreach (Item item in Character.Controlled.Inventory.Items)
{
if (item == null) { continue; }
if (Character.Controlled.HasEquippedItem(item))
{
item.DrawHUD(spriteBatch, cam, Character.Controlled);
}
}
}
if (IsCampaignInterfaceOpen) { return; }
@@ -415,7 +426,7 @@ namespace Barotrauma
Vector2 startPos = character.DrawPosition + (character.FocusedCharacter.DrawPosition - character.DrawPosition) * 0.7f;
startPos = cam.WorldToScreen(startPos);
string focusName = character.FocusedCharacter.DisplayName;
string focusName = character.FocusedCharacter.Info == null ? character.FocusedCharacter.DisplayName : character.FocusedCharacter.Info.DisplayName;
Vector2 textPos = startPos;
Vector2 textSize = GUI.Font.MeasureString(focusName);
Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(focusName);
@@ -431,6 +442,14 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUI.SubHeadingFont);
textPos.X += 10.0f * GUI.Scale;
textPos.Y += GUI.SubHeadingFont.MeasureString(focusName).Y;
if (!character.FocusedCharacter.IsIncapacitated && character.FocusedCharacter.IsPet)
{
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("PlayHint", GameMain.Config.KeyBindText(InputType.Use)),
GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
textPos.Y += largeTextSize.Y;
}
if (character.FocusedCharacter.CanBeDragged)
{
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBindText(InputType.Grab)),
@@ -458,6 +477,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 +487,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 +500,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;

View File

@@ -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)

View File

@@ -1,4 +1,5 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
@@ -83,6 +84,10 @@ namespace Barotrauma
{
newMem.interact = FocusedCharacter.ID;
}
else if (newMem.states.HasFlag(InputNetFlags.Use) && (FocusedCharacter?.IsPet ?? false))
{
newMem.interact = FocusedCharacter.ID;
}
else if (focusedItem != null && !CharacterInventory.DraggingItemToWorld &&
!newMem.states.HasFlag(InputNetFlags.Grab) && !newMem.states.HasFlag(InputNetFlags.Health))
{
@@ -253,6 +258,7 @@ namespace Barotrauma
if (readStatus)
{
ReadStatus(msg);
(AIController as EnemyAIController)?.PetBehavior?.ClientRead(msg);
}
msg.ReadPadBits();
@@ -444,14 +450,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);
}

View File

@@ -6,7 +6,7 @@ namespace Barotrauma
{
public enum SoundType
{
Idle, Attack, Die, Damage
Idle, Attack, Die, Damage, Happy, Unhappy
}
private readonly RoundSound roundSound;

View File

@@ -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;

View File

@@ -225,7 +225,15 @@ namespace Barotrauma
Character.Controlled.ResetInteract = true;
if (openHealthWindow != null)
{
openHealthWindow.characterName.Text = value.Character.Name;
if (value.Character.Info == null || Character.Controlled.HasEquippedItem("healthscanner"))
{
openHealthWindow.characterName.Text = value.Character.Name;
}
else
{
openHealthWindow.characterName.Text = value.Character.Info.DisplayName;
}
if (Character.Controlled.SelectedConstruction != null && Character.Controlled.SelectedConstruction.GetComponent<Ladder>() == null)
{
Character.Controlled.SelectedConstruction = null;
@@ -545,7 +553,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 +619,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 +696,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 +1179,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 +1707,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 +1818,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 +1869,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;

View File

@@ -8,5 +8,12 @@
get;
private set;
}
[Serialize("", false), Editable]
public string DamageParticle
{
get;
private set;
}
}
}

View File

@@ -278,17 +278,7 @@ namespace Barotrauma
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams));
break;
case "conditionalsprite":
ISerializableEntity targetEntity;
string target = subElement.GetAttributeString("target", null);
if (string.Equals(target, "character", StringComparison.OrdinalIgnoreCase))
{
targetEntity = character;
}
else
{
targetEntity = this;
}
var conditionalSprite = new ConditionalSprite(subElement, targetEntity, file: GetSpritePath(subElement, null));
var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(), file: GetSpritePath(subElement, null));
ConditionalSprites.Add(conditionalSprite);
if (conditionalSprite.DeformableSprite != null)
{
@@ -300,7 +290,7 @@ namespace Barotrauma
CreateDeformations(subElement);
break;
case "lightsource":
LightSource = new LightSource(subElement)
LightSource = new LightSource(subElement, GetConditionalTarget())
{
ParentBody = body,
SpriteScale = Vector2.One * Scale * TextureScale
@@ -310,6 +300,21 @@ namespace Barotrauma
break;
}
ISerializableEntity GetConditionalTarget()
{
ISerializableEntity targetEntity;
string target = subElement.GetAttributeString("target", null);
if (string.Equals(target, "character", StringComparison.OrdinalIgnoreCase))
{
targetEntity = character;
}
else
{
targetEntity = this;
}
return targetEntity;
}
void CreateDeformations(XElement e)
{
foreach (XElement animationElement in e.GetChildElements("spritedeformation"))
@@ -341,6 +346,7 @@ namespace Barotrauma
}
}
}
LightSource?.CheckConditionals();
}
public void RecreateSprites()
@@ -459,18 +465,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 +492,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)
@@ -554,6 +567,11 @@ namespace Barotrauma
}
}
foreach (var conditionalSprite in ConditionalSprites)
{
conditionalSprite.CheckConditionals();
}
if (LightSource != null)
{
LightSource.ParentSub = body.Submarine;
@@ -566,6 +584,7 @@ namespace Barotrauma
{
LightSource.DeformableLightSprite.Sprite.Depth = ActiveSprite.Depth;
}
LightSource.CheckConditionals();
}
UpdateSpriteStates(deltaTime);
@@ -930,6 +949,10 @@ namespace Barotrauma
{
depth -= depthStep;
}
if (wearableItemComponent.AllowedSlots.Contains(InvSlotType.Bag))
{
depth -= depthStep * 2;
}
wearableColor = wearableItemComponent.Item.GetSpriteColor();
}
float textureScale = wearable.InheritTextureScale ? TextureScale : 1;

View File

@@ -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());
}
},
() =>

View File

@@ -271,7 +271,7 @@ namespace Barotrauma
private static List<GUIButton> CreateConversation(GUIListBox parentBox, string text, Character speaker, IEnumerable<string> options, bool drawChathead = true)
{
var content = new GUILayoutGroup(new RectTransform(Vector2.One, parentBox.Content.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
var content = new GUILayoutGroup(new RectTransform(Vector2.One, parentBox.Content.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true)
{
Stretch = true,
CanBeFocused = true,
@@ -289,7 +289,7 @@ namespace Barotrauma
});
}
var textContent = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), content.RectTransform), childAnchor: Anchor.TopCenter)
var textContent = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0f), content.RectTransform), childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = GUI.IntScale(5)
};
@@ -316,7 +316,7 @@ namespace Barotrauma
content.Recalculate();
textContent.Recalculate();
textBlock.CalculateHeightFromText();
textBlock.RectTransform.MinSize = new Point(0, (int)(textBlock.Rect.Height * 1.2f));
textBlock.RectTransform.MinSize = new Point(0, textBlock.Rect.Height);
foreach (GUIButton btn in buttons)
{
btn.TextBlock.SetTextPos();
@@ -324,10 +324,12 @@ namespace Barotrauma
btn.RectTransform.MinSize = new Point(0, (int)(btn.TextBlock.Rect.Height * 1.2f));
}
textContent.RectTransform.MinSize = new Point(0, textContent.Children.Sum(c => c.Rect.Height + textContent.AbsoluteSpacing) + GUI.IntScale(16));
textContent.RectTransform.MinSize = new Point(0, textContent.Children.Sum(c => c.Rect.Height) + GUI.IntScale(16));
content.RectTransform.MinSize = new Point(0, content.Children.Sum(c => c.Rect.Height));
// Recalculate the text size as it is scaled up and no longer matching the text height due to the textContent's minSize increasing
textBlock.CalculateHeightFromText();
textBlock.TextAlignment = Alignment.TopLeft;
//content.RectTransform.MinSize = new Point(0, textContent.Rect.Height);
return buttons;

View File

@@ -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)
@@ -650,6 +650,8 @@ namespace Barotrauma
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{
radio.Channel = channel;
GameMain.Client?.CreateEntityEvent(radio.Item, new object[] { NetEntityEvent.Type.ChangeProperty, radio.SerializableProperties["channel"] });
if (setText)
{
string text = radio.Channel.ToString().PadLeft(4, '0');

View File

@@ -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); }

View File

@@ -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;

View File

@@ -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))

View File

@@ -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; }

View File

@@ -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()

View File

@@ -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
{

View File

@@ -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)
{

View File

@@ -348,7 +348,7 @@ namespace Barotrauma
spriteBatch.Draw(currSplashScreen.GetTexture(), new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 500) && GameMain.WindowActive && (PlayerInput.KeyHit(Keys.Space) || PlayerInput.KeyHit(Keys.Enter) || PlayerInput.PrimaryMouseButtonDown()))
if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 500) && GameMain.WindowActive && (PlayerInput.KeyHit(Keys.Escape) || PlayerInput.KeyHit(Keys.Space) || PlayerInput.KeyHit(Keys.Enter) || PlayerInput.PrimaryMouseButtonDown()))
{
currSplashScreen.Dispose(); currSplashScreen = null;
}

View File

@@ -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;
}

View File

@@ -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(); }
}
}
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}
};
@@ -251,12 +261,7 @@ namespace Barotrauma
var sub = Character.Controlled.Submarine;
if (sub == null || sub.TeamID != Character.Controlled.TeamID || sub.Info.IsWreck) { return false; }
SetCharacterOrder(null, order, null, Character.Controlled);
var visibleHulls = new List<Hull>(Character.Controlled.GetVisibleHulls());
foreach (var hull in visibleHulls)
{
HumanAIController.PropagateHullSafety(Character.Controlled, hull);
HumanAIController.RefreshTargets(Character.Controlled, order, hull);
}
if (IsSinglePlayer) { HumanAIController.ReportProblem(Character.Controlled, order); }
return true;
},
UserData = order,
@@ -286,7 +291,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 +323,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 +572,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 +588,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 +680,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 +696,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 +1293,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 +1527,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 +1593,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 +1777,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 +2061,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 +2079,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 +2089,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 +2098,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 +2107,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 +2116,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 +2135,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 +2158,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 +2166,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 +2181,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 +2200,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 +2216,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 => c.Item.HasTag(operateWeaponsPrefab.TargetItems)) ??
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems));
if (turret != null) { contextualOrders.Add(new Order(operateWeaponsPrefab, turret.Item, turret, Character.Controlled)); }
}
@@ -2214,14 +2225,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 +2249,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 +2279,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 +2300,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 => c.Item.HasTag(operateWeaponsPrefab.TargetItems)) ||
item.GetConnectedComponents<Turret>(recursive: true).Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)));
}
private GUIButton CreateOrderNode(Point size, RectTransform parent, Point offset, Order order, int hotkey, bool disableNode = false, bool checkIfOrderCanBeHeard = true)
@@ -2462,7 +2484,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 +2501,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 +2550,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 +2776,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 +2932,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 +3019,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 +3027,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));
}

View File

@@ -98,6 +98,9 @@ namespace Barotrauma
case "pendingupgrades":
UpgradeManager = new UpgradeManager(this, subElement, isSingleplayer: true);
break;
case "pets":
petsElement = subElement;
break;
}
}
@@ -213,6 +216,10 @@ namespace Barotrauma
crewDead = false;
endTimer = 5.0f;
CrewManager.InitSinglePlayerRound();
if (petsElement != null)
{
PetBehavior.LoadPets(petsElement);
}
}
protected override void LoadInitialLevel()
@@ -413,8 +420,6 @@ namespace Barotrauma
//--------------------------------------
bool save = false;
if (success)
{
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
@@ -707,6 +712,10 @@ namespace Barotrauma
}
}
petsElement = new XElement("pets");
PetBehavior.SavePets(petsElement);
modeElement.Add(petsElement);
CrewManager.Save(modeElement);
CampaignMetadata.Save(modeElement);
Map.Save(modeElement);

View File

@@ -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
@@ -186,8 +186,7 @@ namespace Barotrauma.Tutorials
// TODO: Rework order highlighting for new command UI
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_mechanic, "repairsystems", highlightColor, new Vector2(5, 5));
//HighlightOrderOption("jobspecific");
}
while (!HasOrder(captain_mechanic, "repairsystems"));
} while (!HasOrder(captain_mechanic, "repairsystems") && !HasOrder(captain_mechanic, "repairmechanical") && !HasOrder(captain_mechanic, "repairelectrical"));
RemoveCompletedObjective(segments[1]);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(2, GameMain.Config.KeyBindText(InputType.Command));

View File

@@ -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);

View File

@@ -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;
@@ -123,7 +123,7 @@ namespace Barotrauma.Tutorials
// Room 3
engineer_reactorObjectiveSensor = Item.ItemList.Find(i => i.HasTag("engineer_reactorobjectivesensor")).GetComponent<MotionSensor>();
tutorial_oxygenGenerator = Item.ItemList.Find(i => i.HasTag("tutorial_oxygengenerator")).GetComponent<Powered>();
tutorial_oxygenGenerator = Item.ItemList.Find(i => i.HasTag("tutorial_oxygengenerator")).GetComponent<OxygenGenerator>();
engineer_reactor = Item.ItemList.Find(i => i.HasTag("engineer_reactor")).GetComponent<Reactor>();
engineer_reactor.FireDelay = engineer_reactor.MeltdownDelay = float.PositiveInfinity;
engineer_reactor.FuelConsumptionRate = 0.0f;
@@ -311,6 +311,8 @@ namespace Barotrauma.Tutorials
}
yield return null;
} while (engineer_reactor.AvailableFuel == 0);
RemoveCompletedObjective(segments[1]);
TriggerTutorialSegment(2);
CoroutineManager.StartCoroutine(ReactorOperatedProperly());
do
{
@@ -352,7 +354,7 @@ namespace Barotrauma.Tutorials
} while (wait > 0.0f);
engineer.SelectedConstruction = null;
engineer_reactor.CanBeSelected = false;
RemoveCompletedObjective(segments[1]);
RemoveCompletedObjective(segments[2]);
SetHighlight(engineer_reactor.Item, false);
SetHighlight(engineer_brokenJunctionBox, true);
SetDoorAccess(engineer_secondDoor, engineer_secondDoorLight, true);
@@ -361,7 +363,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!engineer_secondDoor.IsOpen);
yield return new WaitForSeconds(1f, false);
Repairable repairableJunctionBoxComponent = engineer_brokenJunctionBox.GetComponent<Repairable>();
TriggerTutorialSegment(2, GameMain.Config.KeyBindText(InputType.Select)); // Repair the junction box
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Select)); // Repair the junction box
do
{
if (!engineer.HasEquippedItem("screwdriver"))
@@ -378,7 +380,7 @@ namespace Barotrauma.Tutorials
yield return null;
} while (engineer_brokenJunctionBox.Condition < repairableJunctionBoxComponent.RepairThreshold); // Wait until repaired
SetHighlight(engineer_brokenJunctionBox, false);
RemoveCompletedObjective(segments[2]);
RemoveCompletedObjective(segments[3]);
SetDoorAccess(engineer_thirdDoor, engineer_thirdDoorLight, true);
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
{
@@ -389,14 +391,14 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!engineer_thirdDoor.IsOpen);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.FaultyWiring"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Use), GameMain.Config.KeyBindText(InputType.Deselect)); // Connect the junction boxes
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Use), GameMain.Config.KeyBindText(InputType.Deselect)); // Connect the junction boxes
do { CheckGhostWires(); HandleJunctionBoxWiringHighlights(); yield return null; } while (engineer_workingPump.Voltage < engineer_workingPump.MinVoltage); // Wait until connected all the way to the pump
CheckGhostWires();
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
{
SetHighlight(engineer_disconnectedJunctionBoxes[i].Item, false);
}
RemoveCompletedObjective(segments[3]);
RemoveCompletedObjective(segments[4]);
do { yield return null; } while (engineer_workingPump.Item.CurrentHull.WaterPercentage > waterVolumeBeforeOpening); // Wait until drained
wiringActive = false;
SetDoorAccess(engineer_fourthDoor, engineer_fourthDoorLight, true);
@@ -406,7 +408,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!tutorial_enteredSubmarineSensor.MotionDetected);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Submarine"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(4); // Repair junction box
TriggerTutorialSegment(5); // Repair junction box
while (ContentRunning) yield return null;
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_1, engineer_repairIcon, engineer_repairIconColor);
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_2, engineer_repairIcon, engineer_repairIconColor);
@@ -422,16 +424,16 @@ namespace Barotrauma.Tutorials
// Remove highlights when each individual machine is repaired
do { CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3); yield return null; } while (engineer_submarineJunctionBox_1.Condition < repairableJunctionBoxComponent1.RepairThreshold || engineer_submarineJunctionBox_2.Condition < repairableJunctionBoxComponent2.RepairThreshold || engineer_submarineJunctionBox_3.Condition < repairableJunctionBoxComponent3.RepairThreshold);
CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3);
RemoveCompletedObjective(segments[4]);
RemoveCompletedObjective(segments[5]);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(5); // Powerup reactor
TriggerTutorialSegment(6); // Powerup reactor
SetHighlight(engineer_submarineReactor.Item, true);
engineer.AddActiveObjectiveEntity(engineer_submarineReactor.Item, engineer_reactorIcon, engineer_reactorIconColor);
do { yield return null; } while (!IsReactorPoweredUp(engineer_submarineReactor)); // Wait until ~matches load
engineer.RemoveActiveObjectiveEntity(engineer_submarineReactor.Item);
SetHighlight(engineer_submarineReactor.Item, false);
RemoveCompletedObjective(segments[5]);
RemoveCompletedObjective(segments[6]);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Complete"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(4f, false);

View File

@@ -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);
@@ -141,7 +141,7 @@ namespace Barotrauma.Tutorials
mechanic_brokenWall_1.SpriteColor = Color.White;
for (int i = 0; i < mechanic_brokenWall_1.SectionCount; i++)
{
mechanic_brokenWall_1.AddDamage(i, 165);
mechanic_brokenWall_1.AddDamage(i, 85);
}
mechanic_brokenhull_1 = mechanic_brokenWall_1.Sections[0].gap.FlowTargetHull;
@@ -199,7 +199,7 @@ namespace Barotrauma.Tutorials
mechanic_brokenWall_2.SpriteColor = Color.White;
for (int i = 0; i < mechanic_brokenWall_2.SectionCount; i++)
{
mechanic_brokenWall_2.AddDamage(i, 165);
mechanic_brokenWall_2.AddDamage(i, 85);
}
mechanic_brokenhull_2 = mechanic_brokenWall_2.Sections[0].gap.FlowTargetHull;
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false);

View File

@@ -362,10 +362,10 @@ namespace Barotrauma.Tutorials
SetHighlight(officer_rangedWeaponHolder.Item, false);
do
{
HighlightInventorySlot(officer.Inventory, "harpoongun", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlot(officer.Inventory, "shotgun", highlightColor, 0.5f, 0.5f, 0f);
yield return null;
} while (!officer.HasEquippedItem("harpoongun")); // Wait until equipped
ItemContainer harpoonGunChamber = officer.Inventory.FindItemByIdentifier("harpoongun").GetComponent<ItemContainer>();
} while (!officer.HasEquippedItem("shotgun")); // Wait until equipped
ItemContainer shotGunChamber = officer.Inventory.FindItemByIdentifier("shotgun").GetComponent<ItemContainer>();
SetHighlight(officer_rangedWeaponCabinet.Item, true);
do
{
@@ -376,7 +376,7 @@ namespace Barotrauma.Tutorials
for (int i = 0; i < officer_rangedWeaponCabinet.Inventory.Items.Length; i++)
{
if (officer_rangedWeaponCabinet.Inventory.Items[i] == null) continue;
if (officer_rangedWeaponCabinet.Inventory.Items[i].Prefab.Identifier == "spear")
if (officer_rangedWeaponCabinet.Inventory.Items[i].Prefab.Identifier == "shotgunshell")
{
HighlightInventorySlot(officer_rangedWeaponCabinet.Inventory, i, highlightColor, 0.5f, 0.5f, 0f);
}
@@ -387,18 +387,18 @@ namespace Barotrauma.Tutorials
for (int i = 0; i < officer.Inventory.Items.Length; i++)
{
if (officer.Inventory.Items[i] == null) continue;
if (officer.Inventory.Items[i].Prefab.Identifier == "spear")
if (officer.Inventory.Items[i].Prefab.Identifier == "shotgunshell")
{
HighlightInventorySlot(officer.Inventory, i, highlightColor, 0.5f, 0.5f, 0f);
}
}
if (officer.Inventory.FindItemByIdentifier("spear") != null || (IsSelectedItem(officer_rangedWeaponCabinet.Item) && officer_rangedWeaponCabinet.Inventory.FindItemByIdentifier("spear") != null))
if (officer.Inventory.FindItemByIdentifier("shotgunshell") != null || (IsSelectedItem(officer_rangedWeaponCabinet.Item) && officer_rangedWeaponCabinet.Inventory.FindItemByIdentifier("shotgunshell") != null))
{
HighlightInventorySlot(officer.Inventory, "harpoongun", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlot(officer.Inventory, "shotgun", highlightColor, 0.5f, 0.5f, 0f);
}
yield return null;
} while (!harpoonGunChamber.Inventory.IsFull()); // Wait until all six harpoons loaded
} while (!shotGunChamber.Inventory.IsFull()); // Wait until all six harpoons loaded
RemoveCompletedObjective(segments[5]);
SetHighlight(officer_rangedWeaponCabinet.Item, false);
SetDoorAccess(officer_fourthDoor, officer_fourthDoorLight, true);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -51,12 +51,13 @@ 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/CommandUIAtlas.png", new Rectangle(639, 926, 128,80)));
}
return limbSlotIcons;
}
}
public const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head;
public const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Bag | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head;
private Point screenResolution;
@@ -143,17 +144,13 @@ namespace Barotrauma
protected override ItemInventory GetActiveEquippedSubInventory(int slotIndex)
{
var item = Items[slotIndex];
if (item == null) return null;
if (item == null) { return null; }
var container = item.GetComponent<ItemContainer>();
if (container == null ||
!character.CanAccessInventory(container.Inventory) ||
!container.KeepOpenWhenEquipped ||
!character.HasEquippedItem(container.Item))
if (container == null || !container.KeepOpenWhenEquippedBy(character))
{
return null;
}
return container.Inventory;
}
@@ -625,7 +622,7 @@ namespace Barotrauma
{
var itemContainer = item.GetComponent<ItemContainer>();
if (itemContainer != null &&
itemContainer.KeepOpenWhenEquipped &&
itemContainer.KeepOpenWhenEquippedBy(character) &&
character.CanAccessInventory(itemContainer.Inventory) &&
!highlightedSubInventorySlots.Any(s => s.Inventory == itemContainer.Inventory))
{
@@ -729,6 +726,7 @@ namespace Barotrauma
{
for (int i = 0; i < indicators.Length; i++)
{
if (indicatorIndexes[i] < 0) { continue; }
Item item = Items[indicatorIndexes[i]];
if (item != null)
{
@@ -922,12 +920,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 +1065,7 @@ namespace Barotrauma
}
draggingItem = null;
GUI.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail);
SoundPlayer.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail);
}
public void DrawOwn(SpriteBatch spriteBatch)

View File

@@ -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); }
}
}

View File

@@ -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

View File

@@ -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()
{

View File

@@ -70,6 +70,7 @@ namespace Barotrauma.Items.Components
[Serialize(false, false, description: "Should the inventory of this item be kept open when the item is equipped by a character.")]
public bool KeepOpenWhenEquipped { get; set; }
[Serialize(false, false, description: "Can the inventory of this item be moved around on the screen by the player.")]
public bool MovableFrame { get; set; }
@@ -162,6 +163,30 @@ namespace Barotrauma.Items.Components
}
}
public bool KeepOpenWhenEquippedBy(Character character)
{
if (!character.CanAccessInventory(Inventory) ||
!KeepOpenWhenEquipped ||
!character.HasEquippedItem(Item))
{
return false;
}
//if holding 2 different "always open" items in different hands, don't force them to stay open
if (character.SelectedItems[0] != null &&
character.SelectedItems[1] != null &&
character.SelectedItems[0] != character.SelectedItems[1])
{
if ((character.SelectedItems[0].GetComponent<ItemContainer>()?.KeepOpenWhenEquipped ?? false) &&
(character.SelectedItems[1].GetComponent<ItemContainer>()?.KeepOpenWhenEquipped ?? false))
{
return false;
}
}
return true;
}
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{
if (hideItems || (item.body != null && !item.body.Enabled)) { return; }

View File

@@ -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;

View File

@@ -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)
{

View File

@@ -284,6 +284,7 @@ namespace Barotrauma.Items.Components
{
Enabled = false,
Selected = AutoTemp,
ClickSound = GUISoundType.UISwitch,
OnClicked = (button, data) =>
{
AutoTemp = !AutoTemp;

View File

@@ -181,6 +181,7 @@ namespace Barotrauma.Items.Components
{
Selected = false,
Enabled = true,
ClickSound = GUISoundType.UISwitch,
OnClicked = (button, data) =>
{
button.Selected = !button.Selected;

View File

@@ -123,6 +123,7 @@ namespace Barotrauma.Items.Components
{
Selected = autoPilot,
Enabled = true,
ClickSound = GUISoundType.UISwitch,
OnClicked = (button, data) =>
{
button.Selected = !button.Selected;

View File

@@ -342,7 +342,8 @@ namespace Barotrauma.Items.Components
}
else
{
if (Vector2.DistanceSquared(nodeWorldPos, draggingWire.nodes[(int)highlightedNodeIndex]) > Submarine.GridSize.X * Submarine.GridSize.X || PlayerInput.IsShiftDown())
if ((highlightedNodeIndex.HasValue && Vector2.DistanceSquared(nodeWorldPos, draggingWire.nodes[(int)highlightedNodeIndex]) > Submarine.GridSize.X * Submarine.GridSize.X) ||
PlayerInput.IsShiftDown())
{
selectedNodeIndex = highlightedNodeIndex;
}
@@ -535,6 +536,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)

View File

@@ -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;
@@ -285,7 +286,7 @@ namespace Barotrauma.Items.Components
rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
if (!editing || GUI.DisableHUD) { return; }
if (!editing || GUI.DisableHUD || !item.IsSelected) { return; }
float widgetRadius = 60.0f;
@@ -304,10 +305,12 @@ namespace Barotrauma.Items.Components
drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * widgetRadius,
Color.LightGreen);
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 +320,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 +352,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 +369,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) =>
{

View File

@@ -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];
@@ -398,8 +404,8 @@ namespace Barotrauma
container = (this as ItemInventory).Container;
}
if (container == null) return false;
return owner.SelectedCharacter != null || !container.KeepOpenWhenEquipped || (!(owner is Character)) || !owner.HasEquippedItem(container.Item);
if (container == null) { return false; }
return owner.SelectedCharacter != null|| (!(owner is Character character)) || !container.KeepOpenWhenEquippedBy(character) || !owner.HasEquippedItem(container.Item);
}
protected virtual bool HideSlot(int i)
@@ -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();
}
}
@@ -993,13 +1002,23 @@ namespace Barotrauma
{
if (DraggingItemToWorld &&
Character.Controlled.FocusedItem?.OwnInventory != null &&
(Character.Controlled.FocusedItem.GetComponent<ItemContainer>()?.HasRequiredItems(Character.Controlled, addMessage: false) ?? false) &&
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 +1044,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 +1072,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)

View File

@@ -62,7 +62,7 @@ namespace Barotrauma
}
}
public override bool DrawBelowWater => (!(Screen.Selected is SubEditorScreen editor) || !editor.WiringMode || !isWire) && base.DrawBelowWater;
public override bool DrawBelowWater => (!(Screen.Selected is SubEditorScreen editor) || !editor.WiringMode || !isWire) && (base.DrawBelowWater || ParentInventory is CharacterInventory);
public override bool DrawOverWater => base.DrawOverWater || (IsSelected || Screen.Selected is SubEditorScreen editor && editor.WiringMode) && isWire;
@@ -332,6 +332,7 @@ namespace Barotrauma
var holdable = GetComponent<Holdable>();
if (holdable != null && holdable.Picker?.AnimController != null)
{
if (!back) { return; }
float depthStep = 0.000001f;
if (holdable.Picker.SelectedItems[0] == this)
{
@@ -542,7 +543,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 +664,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)
@@ -855,7 +856,7 @@ namespace Barotrauma
public void UpdateHUD(Camera cam, Character character, float deltaTime)
{
bool editingHUDCreated = false;
if ((HasInGameEditableProperties && character.SelectedConstruction == this) ||
if ((HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped)) ||
Screen.Selected == GameMain.SubEditorScreen)
{
GUIComponent prevEditingHUD = editingHUD;
@@ -956,7 +957,7 @@ namespace Barotrauma
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character)
{
if (HasInGameEditableProperties)
if (HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped))
{
DrawEditing(spriteBatch, cam);
}
@@ -1028,13 +1029,13 @@ namespace Barotrauma
}
else
{
if (HasInGameEditableProperties)
if (HasInGameEditableProperties && Character.Controlled != null && (Character.Controlled.SelectedConstruction == this || EditableWhenEquipped))
{
if (editingHUD != null && editingHUD.UserData == this) { editingHUD.AddToGUIUpdateList(); }
}
}
if (Character.Controlled != null && Character.Controlled?.SelectedConstruction != this) { return; }
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != this) { return; }
bool needsLayoutUpdate = false;
foreach (ItemComponent ic in activeHUDs)

View File

@@ -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;
}

View File

@@ -110,5 +110,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
static partial void PlayTinnitusProjSpecific(float volume) => SoundPlayer.PlaySound("tinnitus", volume: volume);
}
}

View File

@@ -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);

View File

@@ -10,11 +10,30 @@ namespace Barotrauma
{
partial class Hull : MapEntity, ISerializableEntity, IServerSerializable, IClientSerializable
{
private class RemoteDecal
{
public readonly UInt32 DecalId;
public readonly int SpriteIndex;
public Vector2 NormalizedPos;
public readonly float Scale;
public RemoteDecal(UInt32 decalId, int spriteIndex, Vector2 normalizedPos, float scale)
{
DecalId = decalId;
SpriteIndex = spriteIndex;
NormalizedPos = normalizedPos;
Scale = scale;
}
}
private float serverUpdateDelay;
private float remoteWaterVolume, remoteOxygenPercentage;
private List<Vector3> remoteFireSources;
private readonly List<BackgroundSection> remoteBackgroundSections = new List<BackgroundSection>();
private readonly List<RemoteDecal> remoteDecals = new List<RemoteDecal>();
private readonly HashSet<Decal> pendingDecalUpdates = new HashSet<Decal>();
private double lastAmbientLightEditTime;
public override bool SelectableInEditor
@@ -94,20 +113,21 @@ namespace Barotrauma
{
if (entity == this || !entity.IsHighlighted) { continue; }
if (!entity.IsMouseOn(position)) { continue; }
if (entity.linkedTo != null && entity.linkedTo.Contains(this))
if (entity.linkedTo == null || !entity.Linkable) { continue; }
if (entity.linkedTo.Contains(this) || linkedTo.Contains(entity) || rClick)
{
if (entity == this || !entity.IsHighlighted) continue;
if (!entity.IsMouseOn(position)) continue;
if (entity.Linkable && entity.linkedTo != null && !entity.linkedTo.Contains(this))
if (entity == this || !entity.IsHighlighted) { continue; }
if (!entity.IsMouseOn(position)) { continue; }
if (entity.linkedTo.Contains(this))
{
entity.linkedTo.Add(this);
linkedTo.Add(entity);
entity.linkedTo.Remove(this);
linkedTo.Remove(entity);
}
}
else if (entity.Linkable && entity.linkedTo != null)
else
{
entity.linkedTo.Add(this);
linkedTo.Add(entity);
if (!entity.linkedTo.Contains(this)) { entity.linkedTo.Add(this); }
if (!linkedTo.Contains(this)) { linkedTo.Add(entity); }
}
}
}
@@ -125,10 +145,14 @@ namespace Barotrauma
networkUpdateTimer += deltaTime;
if (networkUpdateTimer > 0.2f)
{
if (!pendingSectionUpdates.Any())
if (!pendingSectionUpdates.Any() && !pendingDecalUpdates.Any())
{
GameMain.NetworkMember?.CreateEntityEvent(this);
}
foreach (Decal decal in pendingDecalUpdates)
{
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { decal });
}
foreach (int pendingSectionUpdate in pendingSectionUpdates)
{
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { pendingSectionUpdate });
@@ -261,6 +285,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);
@@ -530,9 +561,9 @@ namespace Barotrauma
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
msg.Write(extraData != null);
if (extraData == null)
{
msg.WriteRangedInteger(0, 0, 2);
msg.WriteRangedSingle(MathHelper.Clamp(waterVolume / Volume, 0.0f, 1.5f), 0.0f, 1.5f, 8);
msg.Write(FireSources.Count > 0);
@@ -552,8 +583,16 @@ namespace Barotrauma
}
}
}
else if (extraData[0] is Decal decal)
{
msg.WriteRangedInteger(1, 0, 2);
int decalIndex = decals.IndexOf(decal);
msg.Write((byte)(decalIndex < 0 ? 255 : decalIndex));
msg.WriteRangedSingle(decal.BaseAlpha, 0.0f, 1.0f, 8);
}
else
{
msg.WriteRangedInteger(2, 0, 2);
int sectorToUpdate = (int)extraData[0];
int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
@@ -598,11 +637,6 @@ namespace Barotrauma
{
float colorStrength = message.ReadRangedSingle(0.0f, 1.0f, 8);
Color color = new Color(message.ReadUInt32());
float prevColorStrength = BackgroundSections[i].ColorStrength;
BackgroundSections[i].SetColorStrength(colorStrength);
BackgroundSections[i].SetColor(color);
paintAmount = Math.Max(0, paintAmount + (BackgroundSections[i].ColorStrength - prevColorStrength) / BackgroundSections.Count);
var remoteBackgroundSection = remoteBackgroundSections.Find(s => s.Index == i);
if (remoteBackgroundSection != null)
{
@@ -619,16 +653,16 @@ namespace Barotrauma
else
{
int decalCount = message.ReadRangedInteger(0, MaxDecalsPerHull);
decals.Clear();
if (decalCount == 0) { decals.Clear(); }
remoteDecals.Clear();
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);
remoteDecals.Add(new RemoteDecal(decalId, spriteIndex, new Vector2(normalizedXPos, normalizedYPos), decalScale));
}
}
}
@@ -642,11 +676,30 @@ namespace Barotrauma
{
foreach (BackgroundSection remoteBackgroundSection in remoteBackgroundSections)
{
float prevColorStrength = BackgroundSections[remoteBackgroundSection.Index].ColorStrength;
BackgroundSections[remoteBackgroundSection.Index].SetColor(remoteBackgroundSection.Color);
BackgroundSections[remoteBackgroundSection.Index].SetColorStrength(remoteBackgroundSection.ColorStrength);
paintAmount = Math.Max(0, paintAmount + (BackgroundSections[remoteBackgroundSection.Index].ColorStrength - prevColorStrength) / BackgroundSections.Count);
}
remoteBackgroundSections.Clear();
if (remoteDecals.Any())
{
decals.Clear();
foreach (RemoteDecal remoteDecal in remoteDecals)
{
float decalPosX = MathHelper.Lerp(rect.X, rect.Right, remoteDecal.NormalizedPos.X);
float decalPosY = MathHelper.Lerp(rect.Y - rect.Height, rect.Y, remoteDecal.NormalizedPos.Y);
if (Submarine != null)
{
decalPosX += Submarine.Position.X;
decalPosY += Submarine.Position.Y;
}
AddDecal(remoteDecal.DecalId, new Vector2(decalPosX, decalPosY), remoteDecal.Scale, isNetworkEvent: true, spriteIndex: remoteDecal.SpriteIndex);
}
remoteDecals.Clear();
}
if (remoteFireSources == null) { return; }
WaterVolume = remoteWaterVolume;

View File

@@ -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;

View File

@@ -1,3 +1,4 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -6,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma.Lights
{
class LightSourceParams : ISerializableEntity
@@ -42,6 +44,12 @@ namespace Barotrauma.Lights
}
}
[Serialize(1f, true), Editable(minValue: 0.01f, maxValue: 100f, ValueStep = 0.1f, DecimalCount = 2)]
public float Scale { get; set; }
[Serialize("0, 0", true), Editable(ValueStep = 1, DecimalCount = 1, MinValueFloat = -1000f, MaxValueFloat = 1000f)]
public Vector2 Offset { get; set; }
public float TextureRange
{
get;
@@ -239,11 +247,13 @@ namespace Barotrauma.Lights
}
}
private Vector2 _spriteScale = Vector2.One;
public Vector2 SpriteScale
{
get;
set;
} = Vector2.One;
get { return _spriteScale * lightSourceParams.Scale; }
set { _spriteScale = value; }
}
public float? OverrideLightSpriteAlpha
{
@@ -278,7 +288,9 @@ namespace Barotrauma.Lights
{
get { return lightSourceParams.LightSprite; }
}
private Vector2 OverrideLightTextureOrigin => OverrideLightTexture.Origin + LightSourceParams.Offset;
public Color Color
{
get { return lightSourceParams.Color; }
@@ -331,16 +343,42 @@ namespace Barotrauma.Lights
public bool Enabled = true;
public LightSource (XElement element)
private ISerializableEntity conditionalTarget;
private readonly PropertyConditional.Comparison comparison;
private readonly List<PropertyConditional> conditionals = new List<PropertyConditional>();
public LightSource (XElement element, ISerializableEntity conditionalTarget = null)
: this(Vector2.Zero, 100.0f, Color.White, null)
{
lightSourceParams = new LightSourceParams(element);
CastShadows = element.GetAttributeBool("castshadows", true);
string comparison = element.GetAttributeString("comparison", null);
if (comparison != null)
{
Enum.TryParse(comparison, ignoreCase: true, out this.comparison);
}
if (lightSourceParams.DeformableLightSpriteElement != null)
{
DeformableLightSprite = new DeformableSprite(lightSourceParams.DeformableLightSpriteElement, invert: true);
}
this.conditionalTarget = conditionalTarget;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "conditional":
foreach (XAttribute attribute in subElement.Attributes())
{
if (PropertyConditional.IsValid(attribute))
{
conditionals.Add(new PropertyConditional(attribute));
}
}
break;
}
}
}
public LightSource(LightSourceParams lightSourceParams)
@@ -363,7 +401,8 @@ namespace Barotrauma.Lights
CastShadows = true;
texture = LightTexture;
diffToSub = new Dictionary<Submarine, Vector2>();
if (addLight) GameMain.LightManager.AddLight(this);
if (addLight) { GameMain.LightManager.AddLight(this); }
}
/// <summary>
@@ -493,7 +532,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;
@@ -535,7 +574,7 @@ namespace Barotrauma.Lights
var overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
Vector2 origin = OverrideLightTexture.Origin;
Vector2 origin = OverrideLightTextureOrigin;
origin /= Math.Max(overrideTextureDims.X, overrideTextureDims.Y);
origin -= Vector2.One * 0.5f;
@@ -844,7 +883,7 @@ namespace Barotrauma.Lights
{
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
Vector2 origin = OverrideLightTexture.Origin;
Vector2 origin = OverrideLightTextureOrigin;
if (LightSpriteEffect == SpriteEffects.FlipHorizontally) { origin.X = OverrideLightTexture.SourceRect.Width - origin.X; }
if (LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = OverrideLightTexture.SourceRect.Height - origin.Y; }
uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
@@ -1031,7 +1070,7 @@ namespace Barotrauma.Lights
{
var overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
Vector2 origin = OverrideLightTexture.Origin;
Vector2 origin = OverrideLightTextureOrigin;
origin /= Math.Max(overrideTextureDims.X, overrideTextureDims.Y);
origin *= TextureRange;
@@ -1059,13 +1098,12 @@ namespace Barotrauma.Lights
if (DeformableLightSprite != null)
{
Vector2 origin = DeformableLightSprite.Origin;
Vector2 origin = DeformableLightSprite.Origin + LightSourceParams.Offset;
Vector2 drawPos = position;
if (ParentSub != null)
{
drawPos += ParentSub.DrawPosition;
}
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
{
origin.X = DeformableLightSprite.Sprite.SourceRect.Width - origin.X;
@@ -1084,7 +1122,7 @@ namespace Barotrauma.Lights
if (LightSprite != null)
{
Vector2 origin = LightSprite.Origin;
Vector2 origin = LightSprite.Origin + LightSourceParams.Offset;
if ((LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally)
{
origin.X = LightSprite.SourceRect.Width - origin.X;
@@ -1128,12 +1166,27 @@ namespace Barotrauma.Lights
GUI.DrawLine(spriteBatch, drawPos - Vector2.One * Range, drawPos + Vector2.One * Range, Color);
GUI.DrawLine(spriteBatch, drawPos - new Vector2(1.0f, -1.0f) * Range, drawPos + new Vector2(1.0f, -1.0f) * Range, Color);
}
}
}
public void CheckConditionals()
{
if (conditionals.None()) { return; }
if (conditionalTarget == null) { return; }
if (comparison == PropertyConditional.Comparison.And)
{
Enabled = conditionals.All(c => c.Matches(conditionalTarget));
}
else
{
Enabled = conditionals.Any(c => c.Matches(conditionalTarget));
}
}
public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform)
{
if (Range < 1.0f || Color.A < 1) { return; }
if (CastShadows)
{
CheckHullsInRange();
@@ -1158,7 +1211,6 @@ namespace Barotrauma.Lights
return;
}
if (NeedsRecalculation)
{
var verts = FindRaycastHits();
@@ -1168,7 +1220,6 @@ namespace Barotrauma.Lights
NeedsRecalculation = false;
}
Vector2 offset = ParentSub == null ? Vector2.Zero : ParentSub.DrawPosition;
lightEffect.World =
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *

View File

@@ -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;
}
}
}

View File

@@ -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)
{

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;

View File

@@ -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,
@@ -1023,7 +1023,10 @@ namespace Barotrauma.Networking
if (Enum.TryParse(splitMsg[0], out disconnectReason)) { disconnectReasonIncluded = true; }
}
if (disconnectMsg == Lidgren.Network.NetConnection.NoResponseMessage)
if (disconnectMsg == Lidgren.Network.NetConnection.NoResponseMessage ||
disconnectReason == DisconnectReason.Banned ||
disconnectReason == DisconnectReason.Kicked ||
disconnectReason == DisconnectReason.TooManyFailedLogins)
{
allowReconnect = false;
}
@@ -1124,7 +1127,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 +1969,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 +2030,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)
@@ -2139,6 +2141,7 @@ namespace Barotrauma.Networking
return;
}
entities.Add(entity);
if (entity != null && (entity is Item || entity is Character || entity is Submarine))
{
entity.ClientRead(objHeader.Value, inc, sendingTime);
@@ -2186,10 +2189,11 @@ 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)
objHeader == ServerNetObject.ENTITY_EVENT || objHeader == ServerNetObject.ENTITY_EVENT_INITIAL ||
objHeader == ServerNetObject.ENTITY_POSITION || prevObjHeader == ServerNetObject.ENTITY_POSITION)
{
foreach (IServerSerializable ent in entities)
{
@@ -2721,7 +2725,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 +2742,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 +2782,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 +2822,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;
}

View File

@@ -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--)
{

View File

@@ -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);
}
}
}
}

View File

@@ -162,6 +162,7 @@ namespace Barotrauma.Networking
disconnectMsg = $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}";
}
Close(disconnectMsg, disableReconnect: true);
OnDisconnectMessageReceived?.Invoke(DisconnectReason.MissingContentPackage + "/" + disconnectMsg);
}
else
{

View File

@@ -373,7 +373,17 @@ namespace Barotrauma.Networking
info.GameMode = element.GetAttributeString("GameMode", "");
info.GameVersion = element.GetAttributeString("GameVersion", "");
info.MaxPlayers = element.GetAttributeInt("MaxPlayers", 0);
int maxPlayersElement = element.GetAttributeInt("MaxPlayers", 0);
if (maxPlayersElement > NetConfig.MaxPlayers)
{
DebugConsole.IsOpen = true;
DebugConsole.NewMessage($"Setting the maximum amount of players to {maxPlayersElement} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", Color.Red);
maxPlayersElement = NetConfig.MaxPlayers;
}
info.MaxPlayers = maxPlayersElement;
if (Enum.TryParse(element.GetAttributeString("PlayStyle", ""), out PlayStyle playStyleTemp)) { info.PlayStyle = playStyleTemp; }
if (bool.TryParse(element.GetAttributeString("UsingWhiteList", ""), out bool whitelistTemp)) { info.UsingWhiteList = whitelistTemp; }

View File

@@ -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());
});
}
}

View File

@@ -1,131 +0,0 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Barotrauma.Particles
{
class Decal
{
public readonly DecalPrefab Prefab;
private Vector2 position;
public readonly Sprite Sprite;
private float fadeTimer;
public float FadeTimer
{
get { return fadeTimer; }
set { fadeTimer = MathHelper.Clamp(value, 0.0f, LifeTime); }
}
public float FadeInTime
{
get { return Prefab.FadeInTime; }
}
public float FadeOutTime
{
get { return Prefab.FadeOutTime; }
}
public float LifeTime
{
get { return Prefab.LifeTime; }
}
public Color Color
{
get;
set;
}
public Vector2 WorldPosition
{
get
{
Vector2 worldPos = position
+ clippedSourceRect.Size.ToVector2() / 2 * scale
+ hull.Rect.Location.ToVector2();
if (hull.Submarine != null) { worldPos += hull.Submarine.DrawPosition; }
return worldPos;
}
}
private Hull hull;
private float scale;
private Rectangle clippedSourceRect;
public Decal(DecalPrefab prefab, float scale, Vector2 worldPosition, Hull hull)
{
Prefab = prefab;
this.hull = hull;
//transform to hull-relative coordinates so we don't have to worry about the hull moving
position = worldPosition - hull.WorldRect.Location.ToVector2();
Vector2 drawPos = position + hull.Rect.Location.ToVector2();
Sprite = prefab.Sprites[Rand.Range(0, prefab.Sprites.Count, Rand.RandSync.Unsynced)];
Color = prefab.Color;
Rectangle drawRect = new Rectangle(
(int)(drawPos.X - Sprite.size.X / 2 * scale),
(int)(drawPos.Y + Sprite.size.Y / 2 * scale),
(int)(Sprite.size.X * scale),
(int)(Sprite.size.Y * scale));
Rectangle overFlowAmount = new Rectangle(
(int)Math.Max(hull.Rect.X - drawRect.X, 0.0f),
(int)Math.Max(drawRect.Y - hull.Rect.Y, 0.0f),
(int)Math.Max(drawRect.Right - hull.Rect.Right, 0.0f),
(int)Math.Max((hull.Rect.Y - hull.Rect.Height) - (drawRect.Y - drawRect.Height), 0.0f));
clippedSourceRect = new Rectangle(
Sprite.SourceRect.X + (int)(overFlowAmount.X / scale),
Sprite.SourceRect.Y + (int)(overFlowAmount.Y / scale),
Sprite.SourceRect.Width - (int)((overFlowAmount.X + overFlowAmount.Width) / scale),
Sprite.SourceRect.Height - (int)((overFlowAmount.Y + overFlowAmount.Height) / scale));
position -= new Vector2(Sprite.size.X / 2 * scale - overFlowAmount.X, -Sprite.size.Y / 2 * scale + overFlowAmount.Y);
this.scale = scale;
}
public void Update(float deltaTime)
{
fadeTimer += deltaTime;
}
public void StopFadeIn()
{
Color *= GetAlpha();
fadeTimer = Prefab.FadeInTime;
}
public void Draw(SpriteBatch spriteBatch, Hull hull, float depth)
{
Vector2 drawPos = position + hull.Rect.Location.ToVector2();
if (hull.Submarine != null) { drawPos += hull.Submarine.DrawPosition; }
drawPos.Y = -drawPos.Y;
spriteBatch.Draw(Sprite.Texture, drawPos, clippedSourceRect, Color * GetAlpha(), 0, Vector2.Zero, scale, SpriteEffects.None, depth);
}
private float GetAlpha()
{
if (fadeTimer < Prefab.FadeInTime)
{
return fadeTimer / Prefab.FadeInTime;
}
else if (fadeTimer > Prefab.LifeTime - Prefab.FadeOutTime)
{
return (Prefab.LifeTime - fadeTimer) / Prefab.FadeOutTime;
}
return 1.0f;
}
}
}

View File

@@ -1,75 +0,0 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;
namespace Barotrauma.Particles
{
class DecalManager
{
public PrefabCollection<DecalPrefab> Prefabs { get; private set; }
public DecalManager()
{
Prefabs = new PrefabCollection<DecalPrefab>();
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.Decals))
{
LoadFromFile(configFile);
}
}
public void LoadFromFile(ContentFile configFile)
{
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { return; }
bool allowOverriding = false;
var mainElement = doc.Root;
if (doc.Root.IsOverride())
{
mainElement = doc.Root.FirstElement();
allowOverriding = true;
}
foreach (XElement sourceElement in mainElement.Elements())
{
var element = sourceElement.IsOverride() ? sourceElement.FirstElement() : sourceElement;
string name = element.Name.ToString().ToLowerInvariant();
if (Prefabs.ContainsKey(name))
{
if (allowOverriding || sourceElement.IsOverride())
{
DebugConsole.NewMessage($"Overriding the existing decal prefab '{name}' using the file '{configFile.Path}'", Color.Yellow);
}
else
{
DebugConsole.ThrowError($"Error in '{configFile.Path}': Duplicate decal prefab '{name}' found in '{configFile.Path}'! Each decal prefab must have a unique name. " +
"Use <override></override> tags to override prefabs.");
continue;
}
}
Prefabs.Add(new DecalPrefab(element, configFile), allowOverriding || sourceElement.IsOverride());
}
}
public void RemoveByFile(string filePath)
{
Prefabs.RemoveByFile(filePath);
}
public Decal CreateDecal(string decalName, float scale, Vector2 worldPosition, Hull hull)
{
if (!Prefabs.ContainsKey(decalName.ToLowerInvariant()))
{
DebugConsole.ThrowError("Decal prefab " + decalName + " not found!");
return null;
}
DecalPrefab prefab = Prefabs[decalName];
return new Decal(prefab, scale, worldPosition, hull);
}
}
}

View File

@@ -1,73 +0,0 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Particles
{
class DecalPrefab : IPrefab, IDisposable
{
public readonly string Name;
public string OriginalName { get { return Name; } }
private string _identifier;
public string Identifier
{
get
{
if (_identifier == null)
{
_identifier = Name.ToLowerInvariant();
}
return _identifier;
}
}
public string FilePath { get; private set; }
public ContentPackage ContentPackage { get; private set; }
public void Dispose()
{
foreach (Sprite spr in Sprites)
{
spr.Remove();
}
Sprites.Clear();
}
public readonly List<Sprite> Sprites;
public readonly Color Color;
public readonly float LifeTime;
public readonly float FadeOutTime;
public readonly float FadeInTime;
public DecalPrefab(XElement element, ContentFile file)
{
Name = element.Name.ToString();
FilePath = file.Path;
ContentPackage = file.ContentPackage;
Sprites = new List<Sprite>();
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().Equals("sprite", StringComparison.OrdinalIgnoreCase))
{
Sprites.Add(new Sprite(subElement));
}
}
Color = new Color(element.GetAttributeVector4("color", Vector4.One));
LifeTime = element.GetAttributeFloat("lifetime", 10.0f);
FadeOutTime = Math.Min(LifeTime, element.GetAttributeFloat("fadeouttime", 1.0f));
FadeInTime = Math.Min(LifeTime - FadeOutTime, element.GetAttributeFloat("fadeintime", 0.0f));
}
}
}

View File

@@ -483,7 +483,7 @@ namespace Barotrauma.Particles
if (prefab.GrowTime > 0.0f && totalLifeTime - lifeTime < prefab.GrowTime)
{
drawSize *= ((totalLifeTime - lifeTime) / prefab.GrowTime);
drawSize *= MathUtils.SmoothStep((totalLifeTime - lifeTime) / prefab.GrowTime);
}
Color currColor = new Color(color.ToVector4() * ColorMultiplier);

View File

@@ -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);
}
}
}

View File

@@ -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:");

View File

@@ -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();
@@ -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
@@ -2780,9 +2786,7 @@ namespace Barotrauma.CharacterEditor
};
// Spacing
new GUIFrame(new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style: null) { CanBeFocused = false };
Vector2 messageBoxRelSize = new Vector2(0.5f, 0.5f);
int messageBoxWidth = GameMain.GraphicsWidth / 2;
int messageBoxHeight = GameMain.GraphicsHeight / 2;
Vector2 messageBoxRelSize = new Vector2(0.5f, 0.7f);
var saveRagdollButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("SaveRagdoll"));
saveRagdollButton.OnClicked += (button, userData) =>
{
@@ -2902,12 +2906,12 @@ namespace Barotrauma.CharacterEditor
{
var box = new GUIMessageBox(GetCharacterEditorTranslation("SaveAnimation"), string.Empty, new string[] { TextManager.Get("Cancel"), TextManager.Get("Save") }, messageBoxRelSize);
var textArea = new GUIFrame(new RectTransform(new Vector2(1, 0.1f), box.Content.RectTransform) { MinSize = new Point(350, 30) }, style: null);
var inputLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), textArea.RectTransform) { MinSize = new Point(250, 30) }, $"{GetCharacterEditorTranslation("ProvideFileName")}: ");
var inputField = new GUITextBox(new RectTransform(new Vector2(0.5f, 1), textArea.RectTransform, Anchor.TopRight) { MinSize = new Point(100, 30) }, CurrentAnimation.Name);
var inputLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), textArea.RectTransform, Anchor.CenterLeft) { MinSize = new Point(250, 30) }, $"{GetCharacterEditorTranslation("ProvideFileName")}: ");
var inputField = new GUITextBox(new RectTransform(new Vector2(0.45f, 1), textArea.RectTransform, Anchor.CenterRight) { MinSize = new Point(100, 30) }, CurrentAnimation.Name);
// Type filtering
var typeSelectionArea = new GUIFrame(new RectTransform(new Vector2(1f, 0.1f), box.Content.RectTransform) { MinSize = new Point(0, 30) }, style: null);
var typeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), typeSelectionArea.RectTransform, Anchor.TopCenter, Pivot.TopRight), $"{GetCharacterEditorTranslation("SelectAnimationType")}: ");
var typeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1), typeSelectionArea.RectTransform, Anchor.TopCenter, Pivot.TopLeft), elementCount: 4);
var typeLabel = new GUITextBlock(new RectTransform(new Vector2(0.45f, 1), typeSelectionArea.RectTransform, Anchor.CenterLeft), $"{GetCharacterEditorTranslation("SelectAnimationType")}: ");
var typeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.45f, 1), typeSelectionArea.RectTransform, Anchor.CenterRight), elementCount: 4);
foreach (object enumValue in Enum.GetValues(typeof(AnimationType)))
{
if (!(enumValue is AnimationType.NotDefined))
@@ -2958,8 +2962,8 @@ namespace Barotrauma.CharacterEditor
deleteButton.Enabled = false;
// Type filtering
var typeSelectionArea = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.1f), loadBox.Content.RectTransform) { MinSize = new Point(0, 30) }, style: null);
var typeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), typeSelectionArea.RectTransform, Anchor.TopCenter, Pivot.TopRight), $"{GetCharacterEditorTranslation("SelectAnimationType")}: ");
var typeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1), typeSelectionArea.RectTransform, Anchor.TopCenter, Pivot.TopLeft), elementCount: 4);
var typeLabel = new GUITextBlock(new RectTransform(new Vector2(0.45f, 1), typeSelectionArea.RectTransform, Anchor.CenterLeft), $"{GetCharacterEditorTranslation("SelectAnimationType")}: ");
var typeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.45f, 1), typeSelectionArea.RectTransform, Anchor.CenterRight), elementCount: 4);
foreach (object enumValue in Enum.GetValues(typeof(AnimationType)))
{
if (!(enumValue is AnimationType.NotDefined))
@@ -3499,12 +3503,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 +4392,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)
@@ -4465,7 +4465,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 +4904,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 +5016,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 +5077,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 +5117,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 +5156,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 +5250,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 +5259,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 +5269,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: () =>
{

View File

@@ -75,6 +75,17 @@ namespace Barotrauma
{
Character.Controlled.SelectedConstruction.AddToGUIUpdateList();
}
if (Character.Controlled?.Inventory != null)
{
foreach (Item item in Character.Controlled.Inventory.Items)
{
if (item == null) { continue; }
if (Character.Controlled.HasEquippedItem(item))
{
item.AddToGUIUpdateList();
}
}
}
if (GameMain.GameSession != null) GameMain.GameSession.AddToGUIUpdateList();

View File

@@ -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();

View File

@@ -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
@@ -913,14 +879,13 @@ namespace Barotrauma
ChildServerRelay.Start(processInfo);
Thread.Sleep(1000); //wait until the server is ready before connecting
GameMain.Client = new GameClient(name, System.Net.IPAddress.Loopback.ToString(), Steam.SteamManager.GetSteamID(), name, ownerKey, true);
GameMain.Client = new GameClient(string.IsNullOrEmpty(GameMain.Config.PlayerName) ? name : GameMain.Config.PlayerName,
System.Net.IPAddress.Loopback.ToString(), Steam.SteamManager.GetSteamID(), name, ownerKey, true);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to start server", e);
}
return true;
}
private bool QuitClicked(GUIButton button, object obj)
@@ -996,7 +961,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 +1027,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;
}
@@ -1139,7 +1104,16 @@ namespace Barotrauma
{
port = settingsDoc.Root.GetAttributeInt("port", port);
queryPort = settingsDoc.Root.GetAttributeInt("queryport", queryPort);
maxPlayers = settingsDoc.Root.GetAttributeInt("maxplayers", maxPlayers);
int maxPlayersElement = settingsDoc.Root.GetAttributeInt("maxplayers", maxPlayers);
if (maxPlayersElement > NetConfig.MaxPlayers)
{
DebugConsole.IsOpen = true;
DebugConsole.NewMessage($"Setting the maximum amount of players to {maxPlayersElement} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", Color.Red);
maxPlayersElement = NetConfig.MaxPlayers;
}
maxPlayers = maxPlayersElement;
karmaEnabled = settingsDoc.Root.GetAttributeBool("karmaenabled", true);
selectedKarmaPreset = settingsDoc.Root.GetAttributeString("karmapreset", "default");
string playStyleStr = settingsDoc.Root.GetAttributeString("playstyle", "Casual");
@@ -1334,7 +1308,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;
}
};
}

View File

@@ -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++)

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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++)
@@ -2460,7 +2409,6 @@ namespace Barotrauma
int min = Math.Min(6, AutoSaveInfo.Root.Elements().Count());
var loadAutoSave = new GUIDropDown(new RectTransform(Vector2.One, deleteButtonHolder.RectTransform, Anchor.BottomCenter), TextManager.Get("LoadAutoSave"), elementCount: min)
{
Enabled = File.Exists(Path.Combine(SubmarineInfo.SavePath, ".AutoSaves", "AutoSave.sub")),
ToolTip = TextManager.Get("LoadAutoSaveTooltip"),
UserData = "loadautosave",
OnSelected = (button, o) =>
@@ -2609,7 +2557,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 +2738,7 @@ namespace Barotrauma
MapEntity.DeselectAll();
MapEntity.FilteredSelectedList.Clear();
ClearUndoBuffer();
CreateDummyCharacter();
if (newMode == Mode.Wiring)
@@ -2922,7 +2871,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 +2884,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 +3184,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 +3199,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 +3218,7 @@ namespace Barotrauma
{
// Place the item into our hands
DraggedItemPrefab = (MapEntityPrefab) obj;
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
break;
}
}
@@ -3265,7 +3226,7 @@ namespace Barotrauma
}
else
{
GUI.PlayUISound(GUISoundType.PickItem);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
MapEntityPrefab.SelectPrefab(obj);
}
@@ -3611,6 +3572,7 @@ namespace Barotrauma
EntityMenu.AddToGUIUpdateList();
showEntitiesPanel.AddToGUIUpdateList();
previouslyUsedPanel.AddToGUIUpdateList();
undoBufferPanel.AddToGUIUpdateList();
entityCountPanel.AddToGUIUpdateList();
TopPanel.AddToGUIUpdateList();
@@ -3655,10 +3617,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 +3625,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 +3789,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 +4088,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 +4158,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())
{
@@ -4273,9 +4375,18 @@ namespace Barotrauma
Submarine.DrawFront(spriteBatch, editing: true, e => ShowThalamus || !(e.prefab?.Category.HasFlag(MapEntityCategory.Thalamus) ?? false));
if (!WiringMode && !IsMouseOnEditorGUI())
{
MapEntityPrefab.Selected?.DrawPlacing(spriteBatch, cam);
MapEntityPrefab.Selected?.DrawPlacing(spriteBatch, cam);
MapEntity.DrawSelecting(spriteBatch, cam);
}
if (dummyCharacter != null && WiringMode)
{
for (int i = 0; i < dummyCharacter.SelectedItems.Length; i++)
{
if (dummyCharacter.SelectedItems[i] == null) { continue; }
if (i > 0 && dummyCharacter.SelectedItems[0] == dummyCharacter.SelectedItems[i]) { continue; }
dummyCharacter.SelectedItems[i].Draw(spriteBatch, editing: false, back: true);
}
}
spriteBatch.End();
if (GameMain.LightManager.LightingEnabled && lightingEnabled)

View File

@@ -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;
}
}
}

View File

@@ -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);

View File

@@ -730,6 +730,7 @@ namespace Barotrauma.Sounds
else
{
playingChannels[i][j].Dispose();
playingChannels[i][j] = null;
}
}
else if (playingChannels[i][j].FadingOutAndDisposing)
@@ -738,6 +739,7 @@ namespace Barotrauma.Sounds
if (playingChannels[i][j].Gain <= 0.0f)
{
playingChannels[i][j].Dispose();
playingChannels[i][j] = null;
}
}
}

View File

@@ -74,12 +74,18 @@ namespace Barotrauma
//ambience
private static Sound waterAmbienceIn, waterAmbienceOut, waterAmbienceMoving;
private static SoundChannel[] waterAmbienceChannels = new SoundChannel[3];
private static readonly SoundChannel[] waterAmbienceChannels = new SoundChannel[3];
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
private static float[] targetFlowLeft, targetFlowRight;
public static List<Sound> FlowSounds = new List<Sound>();
public static List<Sound> SplashSounds = new List<Sound>();
private static SoundChannel[] flowSoundChannels;
@@ -94,11 +100,18 @@ 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 bool firstTimeInMainMenu = true;
private static Sound startUpSound;
public static bool Initialized;
@@ -163,6 +176,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 +264,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,20 +323,32 @@ 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(); }
else { miscSoundList.Add(new KeyValuePair<string, Sound>(g.Key, s)); }
}));
flowSoundChannels?.ForEach(ch => ch?.Dispose());
flowSoundChannels = new SoundChannel[FlowSounds.Count];
flowVolumeLeft = new float[FlowSounds.Count];
flowVolumeRight = new float[FlowSounds.Count];
targetFlowLeft = new float[FlowSounds.Count];
targetFlowRight = new float[FlowSounds.Count];
fireSoundChannels = new SoundChannel[2];
fireVolumeLeft = new float[2];
fireVolumeRight = new float[2];
fireSoundChannels?.ForEach(ch => ch?.Dispose());
fireSoundChannels = new SoundChannel[fireSizes];
fireVolumeLeft = new float[fireSizes];
fireVolumeRight = new float[fireSizes];
miscSounds = miscSoundList.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
@@ -319,7 +360,6 @@ namespace Barotrauma
}
public static void Update(float deltaTime)
{
if (!Initialized) { return; }
@@ -355,6 +395,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,12 +418,13 @@ namespace Barotrauma
UpdateWaterAmbience(ambienceVolume, deltaTime);
UpdateWaterFlowSounds(deltaTime);
UpdateRandomAmbience(deltaTime);
UpdateHullSounds(deltaTime);
UpdateFireSounds(deltaTime);
}
private static void UpdateWaterAmbience(float ambienceVolume, float deltaTime)
{
if (GameMain.SoundManager.Disabled) { return; }
if (GameMain.SoundManager.Disabled || GameMain.GameScreen?.Cam == null) { return; }
//how fast the sub is moving, scaled to 0.0 -> 1.0
float movementSoundVolume = 0.0f;
@@ -385,6 +432,7 @@ namespace Barotrauma
float insideSubFactor = 0.0f;
foreach (Submarine sub in Submarine.Loaded)
{
if (sub == null || sub.Removed) { continue; }
float movementFactor = (sub.Velocity == Vector2.Zero) ? 0.0f : sub.Velocity.Length() / 10.0f;
movementFactor = MathHelper.Clamp(movementFactor, 0.0f, 1.0f);
@@ -429,6 +477,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");
@@ -448,9 +499,12 @@ namespace Barotrauma
private static void UpdateWaterFlowSounds(float deltaTime)
{
if (FlowSounds.Count == 0) { return; }
float[] targetFlowLeft = new float[FlowSounds.Count];
float[] targetFlowRight = new float[FlowSounds.Count];
for (int i = 0; i < targetFlowLeft.Length; i++)
{
targetFlowLeft[i] = 0.0f;
targetFlowRight[i] = 0.0f;
}
Vector2 listenerPos = new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y);
foreach (Gap gap in Gap.GapList)
@@ -490,6 +544,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 +602,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 +625,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 +697,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 +762,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;
}
@@ -815,7 +940,9 @@ namespace Barotrauma
return "editor";
}
if (Screen.Selected != GameMain.GameScreen) { return "menu"; }
if (Screen.Selected != GameMain.GameScreen) { return firstTimeInMainMenu ? "menu" : "default"; }
firstTimeInMainMenu = false;
if (Character.Controlled != null)
@@ -830,6 +957,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;
@@ -845,12 +984,12 @@ namespace Barotrauma
float totalArea = 0.0f;
foreach (Hull hull in Hull.hullList)
{
if (hull.Submarine != targetSubmarine) continue;
if (hull.Submarine != targetSubmarine) { continue; }
floodedArea += hull.WaterVolume;
totalArea += hull.Volume;
}
if (totalArea > 0.0f && floodedArea / totalArea > 0.25f) return "flooded";
if (totalArea > 0.0f && floodedArea / totalArea > 0.25f) { return "flooded"; }
}
float enemyDistThreshold = 5000.0f;
@@ -863,7 +1002,7 @@ namespace Barotrauma
foreach (Character character in Character.CharacterList)
{
if (character.IsDead || !character.Enabled) continue;
if (!(character.AIController is EnemyAIController enemyAI) || (!enemyAI.AttackHumans && !enemyAI.AttackRooms)) continue;
if (!(character.AIController is EnemyAIController enemyAI) || (!enemyAI.AttackHumans && !enemyAI.AttackRooms)) { continue; }
if (targetSubmarine != null)
{
@@ -952,6 +1091,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");
}
}
}
}

View File

@@ -99,6 +99,7 @@ namespace Barotrauma
{
Sprite = new Sprite(element, path, file, lazyLoad: lazyLoad);
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
// TODO: what's the purpose of this?
foreach (XElement subElement in element.Elements())
{
List<PropertyConditional> conditionalList = null;

View File

@@ -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,14 +93,22 @@ 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; }
}
}
else
{
soundChannel.Position = new Vector3(worldPosition, 0.0f);
}
if (soundChannel != null && soundChannel.Looping)
{

View File

@@ -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);
}
}
}

View File

@@ -1,773 +0,0 @@
//
// System.Web.HttpUtility
//
// Authors:
// Patrik Torstensson (Patrik.Torstensson@labs2.com)
// Wictor Wilén (decode/encode functions) (wictor@ibizkit.se)
// Tim Coleman (tim@timcoleman.com)
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using Barotrauma.IO;
using System.Security.Permissions;
using System.Text;
namespace RestSharp.Contrib
{
//#if !MONOTOUCH
// // CAS - no InheritanceDemand here as the class is sealed
// [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
//#endif
public sealed class HttpUtility
{
sealed class HttpQSCollection : NameValueCollection
{
public override string ToString()
{
int count = Count;
if (count == 0)
return "";
StringBuilder sb = new StringBuilder();
string[] keys = AllKeys;
for (int i = 0; i < count; i++)
{
sb.AppendFormat("{0}={1}&", keys[i], this[keys[i]]);
}
if (sb.Length > 0)
sb.Length--;
return sb.ToString();
}
}
#region Constructors
public HttpUtility()
{
}
#endregion // Constructors
#region Methods
/*
public static void HtmlAttributeEncode(string s, TextWriter output)
{
if (output == null)
{
#if NET_4_0
throw new ArgumentNullException ("output");
#else
throw new NullReferenceException(".NET emulation");
#endif
}
#if NET_4_0
HttpEncoder.Current.HtmlAttributeEncode (s, output);
#else
output.Write(HttpEncoder.HtmlAttributeEncode(s));
#endif
}
*/
public static string HtmlAttributeEncode(string s)
{
#if NET_4_0
if (s == null)
return null;
using (var sw = new StringWriter ()) {
HttpEncoder.Current.HtmlAttributeEncode (s, sw);
return sw.ToString ();
}
#else
return HttpEncoder.HtmlAttributeEncode(s);
#endif
}
public static string UrlDecode(string str)
{
return UrlDecode(str, Encoding.UTF8);
}
static char[] GetChars(System.IO.MemoryStream b, Encoding e)
{
return e.GetChars(b.GetBuffer(), 0, (int)b.Length);
}
static void WriteCharBytes(IList buf, char ch, Encoding e)
{
if (ch > 255)
{
foreach (byte b in e.GetBytes(new char[] { ch }))
buf.Add(b);
}
else
buf.Add((byte)ch);
}
public static string UrlDecode(string s, Encoding e)
{
if (null == s)
return null;
if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1)
return s;
if (e == null)
e = Encoding.UTF8;
long len = s.Length;
var bytes = new List<byte>();
int xchar;
char ch;
for (int i = 0; i < len; i++)
{
ch = s[i];
if (ch == '%' && i + 2 < len && s[i + 1] != '%')
{
if (s[i + 1] == 'u' && i + 5 < len)
{
// unicode hex sequence
xchar = GetChar(s, i + 2, 4);
if (xchar != -1)
{
WriteCharBytes(bytes, (char)xchar, e);
i += 5;
}
else
WriteCharBytes(bytes, '%', e);
}
else if ((xchar = GetChar(s, i + 1, 2)) != -1)
{
WriteCharBytes(bytes, (char)xchar, e);
i += 2;
}
else
{
WriteCharBytes(bytes, '%', e);
}
continue;
}
if (ch == '+')
WriteCharBytes(bytes, ' ', e);
else
WriteCharBytes(bytes, ch, e);
}
byte[] buf = bytes.ToArray();
bytes = null;
return e.GetString(buf);
}
public static string UrlDecode(byte[] bytes, Encoding e)
{
if (bytes == null)
return null;
return UrlDecode(bytes, 0, bytes.Length, e);
}
static int GetInt(byte b)
{
char c = (char)b;
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static int GetChar(byte[] bytes, int offset, int length)
{
int value = 0;
int end = length + offset;
for (int i = offset; i < end; i++)
{
int current = GetInt(bytes[i]);
if (current == -1)
return -1;
value = (value << 4) + current;
}
return value;
}
static int GetChar(string str, int offset, int length)
{
int val = 0;
int end = length + offset;
for (int i = offset; i < end; i++)
{
char c = str[i];
if (c > 127)
return -1;
int current = GetInt((byte)c);
if (current == -1)
return -1;
val = (val << 4) + current;
}
return val;
}
public static string UrlDecode(byte[] bytes, int offset, int count, Encoding e)
{
if (bytes == null)
return null;
if (count == 0)
return String.Empty;
if (bytes == null)
throw new ArgumentNullException("bytes");
if (offset < 0 || offset > bytes.Length)
throw new ArgumentOutOfRangeException("offset");
if (count < 0 || offset + count > bytes.Length)
throw new ArgumentOutOfRangeException("count");
StringBuilder output = new StringBuilder();
System.IO.MemoryStream acc = new System.IO.MemoryStream();
int end = count + offset;
int xchar;
for (int i = offset; i < end; i++)
{
if (bytes[i] == '%' && i + 2 < count && bytes[i + 1] != '%')
{
if (bytes[i + 1] == (byte)'u' && i + 5 < end)
{
if (acc.Length > 0)
{
output.Append(GetChars(acc, e));
acc.SetLength(0);
}
xchar = GetChar(bytes, i + 2, 4);
if (xchar != -1)
{
output.Append((char)xchar);
i += 5;
continue;
}
}
else if ((xchar = GetChar(bytes, i + 1, 2)) != -1)
{
acc.WriteByte((byte)xchar);
i += 2;
continue;
}
}
if (acc.Length > 0)
{
output.Append(GetChars(acc, e));
acc.SetLength(0);
}
if (bytes[i] == '+')
{
output.Append(' ');
}
else
{
output.Append((char)bytes[i]);
}
}
if (acc.Length > 0)
{
output.Append(GetChars(acc, e));
}
acc = null;
return output.ToString();
}
public static byte[] UrlDecodeToBytes(byte[] bytes)
{
if (bytes == null)
return null;
return UrlDecodeToBytes(bytes, 0, bytes.Length);
}
public static byte[] UrlDecodeToBytes(string str)
{
return UrlDecodeToBytes(str, Encoding.UTF8);
}
public static byte[] UrlDecodeToBytes(string str, Encoding e)
{
if (str == null)
return null;
if (e == null)
throw new ArgumentNullException("e");
return UrlDecodeToBytes(e.GetBytes(str));
}
public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count)
{
if (bytes == null)
return null;
if (count == 0)
return new byte[0];
int len = bytes.Length;
if (offset < 0 || offset >= len)
throw new ArgumentOutOfRangeException("offset");
if (count < 0 || offset > len - count)
throw new ArgumentOutOfRangeException("count");
System.IO.MemoryStream result = new System.IO.MemoryStream();
int end = offset + count;
for (int i = offset; i < end; i++)
{
char c = (char)bytes[i];
if (c == '+')
{
c = ' ';
}
else if (c == '%' && i < end - 2)
{
int xchar = GetChar(bytes, i + 1, 2);
if (xchar != -1)
{
c = (char)xchar;
i += 2;
}
}
result.WriteByte((byte)c);
}
return result.ToArray();
}
public static string UrlEncode(string str)
{
return UrlEncode(str, Encoding.UTF8);
}
public static string UrlEncode(string s, Encoding Enc)
{
if (s == null)
return null;
if (s == String.Empty)
return String.Empty;
bool needEncode = false;
int len = s.Length;
for (int i = 0; i < len; i++)
{
char c = s[i];
if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z'))
{
if (HttpEncoder.NotEncoded(c))
continue;
needEncode = true;
break;
}
}
if (!needEncode)
return s;
// avoided GetByteCount call
byte[] bytes = new byte[Enc.GetMaxByteCount(s.Length)];
int realLen = Enc.GetBytes(s, 0, s.Length, bytes, 0);
return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, realLen));
}
public static string UrlEncode(byte[] bytes)
{
if (bytes == null)
return null;
if (bytes.Length == 0)
return String.Empty;
return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, bytes.Length));
}
public static string UrlEncode(byte[] bytes, int offset, int count)
{
if (bytes == null)
return null;
if (bytes.Length == 0)
return String.Empty;
return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, offset, count));
}
public static byte[] UrlEncodeToBytes(string str)
{
return UrlEncodeToBytes(str, Encoding.UTF8);
}
public static byte[] UrlEncodeToBytes(string str, Encoding e)
{
if (str == null)
return null;
if (str.Length == 0)
return new byte[0];
byte[] bytes = e.GetBytes(str);
return UrlEncodeToBytes(bytes, 0, bytes.Length);
}
public static byte[] UrlEncodeToBytes(byte[] bytes)
{
if (bytes == null)
return null;
if (bytes.Length == 0)
return new byte[0];
return UrlEncodeToBytes(bytes, 0, bytes.Length);
}
public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count)
{
if (bytes == null)
return null;
#if NET_4_0
return HttpEncoder.Current.UrlEncode (bytes, offset, count);
#else
return HttpEncoder.UrlEncodeToBytes(bytes, offset, count);
#endif
}
public static string UrlEncodeUnicode(string str)
{
if (str == null)
return null;
return Encoding.ASCII.GetString(UrlEncodeUnicodeToBytes(str));
}
public static byte[] UrlEncodeUnicodeToBytes(string str)
{
if (str == null)
return null;
if (str.Length == 0)
return new byte[0];
System.IO.MemoryStream result = new System.IO.MemoryStream(str.Length);
foreach (char c in str)
{
HttpEncoder.UrlEncodeChar(c, result, true);
}
return result.ToArray();
}
/// <summary>
/// Decodes an HTML-encoded string and returns the decoded string.
/// </summary>
/// <param name="s">The HTML string to decode. </param>
/// <returns>The decoded text.</returns>
public static string HtmlDecode(string s)
{
#if NET_4_0
if (s == null)
return null;
using (var sw = new StringWriter ()) {
HttpEncoder.Current.HtmlDecode (s, sw);
return sw.ToString ();
}
#else
return HttpEncoder.HtmlDecode(s);
#endif
}
/// <summary>
/// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream.
/// </summary>
/// <param name="s">The HTML string to decode</param>
/// <param name="output">The TextWriter output stream containing the decoded string. </param>
/*
public static void HtmlDecode(string s, TextWriter output)
{
if (output == null)
{
#if NET_4_0
throw new ArgumentNullException ("output");
#else
throw new NullReferenceException(".NET emulation");
#endif
}
if (!String.IsNullOrEmpty(s))
{
#if NET_4_0
HttpEncoder.Current.HtmlDecode (s, output);
#else
output.Write(HttpEncoder.HtmlDecode(s));
#endif
}
}
*/
public static string HtmlEncode(string s)
{
#if NET_4_0
if (s == null)
return null;
using (var sw = new StringWriter ()) {
HttpEncoder.Current.HtmlEncode (s, sw);
return sw.ToString ();
}
#else
return HttpEncoder.HtmlEncode(s);
#endif
}
/// <summary>
/// HTML-encodes a string and sends the resulting output to a TextWriter output stream.
/// </summary>
/// <param name="s">The string to encode. </param>
/// <param name="output">The TextWriter output stream containing the encoded string. </param>
/*
public static void HtmlEncode(string s, TextWriter output)
{
if (output == null)
{
#if NET_4_0
throw new ArgumentNullException ("output");
#else
throw new NullReferenceException(".NET emulation");
#endif
}
if (!String.IsNullOrEmpty(s))
{
#if NET_4_0
HttpEncoder.Current.HtmlEncode (s, output);
#else
output.Write(HttpEncoder.HtmlEncode(s));
#endif
}
}
*/
#if NET_4_0
public static string HtmlEncode (object value)
{
if (value == null)
return null;
IHtmlString htmlString = value as IHtmlString;
if (htmlString != null)
return htmlString.ToHtmlString ();
return HtmlEncode (value.ToString ());
}
public static string JavaScriptStringEncode (string value)
{
return JavaScriptStringEncode (value, false);
}
public static string JavaScriptStringEncode (string value, bool addDoubleQuotes)
{
if (String.IsNullOrEmpty (value))
return addDoubleQuotes ? "\"\"" : String.Empty;
int len = value.Length;
bool needEncode = false;
char c;
for (int i = 0; i < len; i++) {
c = value [i];
if (c >= 0 && c <= 31 || c == 34 || c == 39 || c == 60 || c == 62 || c == 92) {
needEncode = true;
break;
}
}
if (!needEncode)
return addDoubleQuotes ? "\"" + value + "\"" : value;
var sb = new StringBuilder ();
if (addDoubleQuotes)
sb.Append ('"');
for (int i = 0; i < len; i++) {
c = value [i];
if (c >= 0 && c <= 7 || c == 11 || c >= 14 && c <= 31 || c == 39 || c == 60 || c == 62)
sb.AppendFormat ("\\u{0:x4}", (int)c);
else switch ((int)c) {
case 8:
sb.Append ("\\b");
break;
case 9:
sb.Append ("\\t");
break;
case 10:
sb.Append ("\\n");
break;
case 12:
sb.Append ("\\f");
break;
case 13:
sb.Append ("\\r");
break;
case 34:
sb.Append ("\\\"");
break;
case 92:
sb.Append ("\\\\");
break;
default:
sb.Append (c);
break;
}
}
if (addDoubleQuotes)
sb.Append ('"');
return sb.ToString ();
}
#endif
public static string UrlPathEncode(string s)
{
#if NET_4_0
return HttpEncoder.Current.UrlPathEncode (s);
#else
return HttpEncoder.UrlPathEncode(s);
#endif
}
public static NameValueCollection ParseQueryString(string query)
{
return ParseQueryString(query, Encoding.UTF8);
}
public static NameValueCollection ParseQueryString(string query, Encoding encoding)
{
if (query == null)
throw new ArgumentNullException("query");
if (encoding == null)
throw new ArgumentNullException("encoding");
if (query.Length == 0 || (query.Length == 1 && query[0] == '?'))
return new NameValueCollection();
if (query[0] == '?')
query = query.Substring(1);
NameValueCollection result = new HttpQSCollection();
ParseQueryString(query, encoding, result);
return result;
}
internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result)
{
if (query.Length == 0)
return;
string decoded = HtmlDecode(query);
int decodedLength = decoded.Length;
int namePos = 0;
bool first = true;
while (namePos <= decodedLength)
{
int valuePos = -1, valueEnd = -1;
for (int q = namePos; q < decodedLength; q++)
{
if (valuePos == -1 && decoded[q] == '=')
{
valuePos = q + 1;
}
else if (decoded[q] == '&')
{
valueEnd = q;
break;
}
}
if (first)
{
first = false;
if (decoded[namePos] == '?')
namePos++;
}
string name, value;
if (valuePos == -1)
{
name = null;
valuePos = namePos;
}
else
{
name = UrlDecode(decoded.Substring(namePos, valuePos - namePos - 1), encoding);
}
if (valueEnd < 0)
{
namePos = -1;
valueEnd = decoded.Length;
}
else
{
namePos = valueEnd + 1;
}
value = UrlDecode(decoded.Substring(valuePos, valueEnd - valuePos), encoding);
result.Add(name, value);
if (namePos == -1)
break;
}
}
#endregion // Methods
}
}

View File

@@ -138,14 +138,15 @@ namespace Barotrauma
public static Color GradientLerp(float t, params Color[] gradient)
{
if (!MathUtils.IsValid(t)) { return Color.Purple; }
System.Diagnostics.Debug.Assert(gradient.Length > 0, "Empty color array passed to the GradientLerp method");
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;
}

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.6.2</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)" />

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.6.2</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)" />

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.6.2</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)" />

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.6.2</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)" />

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.6.2</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)" />

View File

@@ -420,6 +420,7 @@ namespace Barotrauma
if (writeStatus)
{
WriteStatus(tempBuffer);
(AIController as EnemyAIController)?.PetBehavior?.ServerWrite(tempBuffer);
HealthUpdatePending = false;
}
@@ -478,7 +479,7 @@ namespace Barotrauma
msg.Write(Info == null);
msg.Write(entityId);
msg.Write(SpeciesName);
msg.Write(seed);
msg.Write(Seed);
if (Removed)
{
@@ -528,19 +529,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
{

View File

@@ -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);
}
}
@@ -1119,6 +1119,12 @@ namespace Barotrauma
}
else
{
if (maxPlayers > NetConfig.MaxPlayers)
{
NewMessage($"Setting the maximum amount of players to {maxPlayers} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.");
maxPlayers = NetConfig.MaxPlayers;
}
GameMain.Server.ServerSettings.MaxPlayers = maxPlayers;
NewMessage("Set the maximum player count to " + maxPlayers + ".");
}
@@ -1132,6 +1138,12 @@ namespace Barotrauma
}
else
{
if (maxPlayers > NetConfig.MaxPlayers)
{
GameMain.Server.SendConsoleMessage($"Setting the maximum amount of players to {maxPlayers} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", client);
maxPlayers = NetConfig.MaxPlayers;
}
GameMain.Server.ServerSettings.MaxPlayers = maxPlayers;
NewMessage(client.Name + " set the maximum player count to " + maxPlayers + ".");
GameMain.Server.SendConsoleMessage("Set the maximum player count to " + maxPlayers + ".", client);
@@ -1352,7 +1364,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);
}
}));

View File

@@ -15,7 +15,7 @@ namespace Barotrauma
msg.Write((byte)monsters.Count);
foreach (Character monster in monsters)
{
monster.WriteSpawnData(msg, monster.ID, restrictMessageSize: false);
monster.WriteSpawnData(msg, monster.OriginalID, restrictMessageSize: false);
}
}
}

View File

@@ -147,6 +147,14 @@ namespace Barotrauma
c.InGame && (IsOwner(c) || c.HasPermission(ClientPermissions.ManageCampaign)));
}
public void LoadPets()
{
if (petsElement != null)
{
PetBehavior.LoadPets(petsElement);
}
}
protected override IEnumerable<object> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults)
{
lastUpdateID++;
@@ -229,6 +237,9 @@ namespace Barotrauma
c.Inventory.DeleteAllItems();
}
petsElement = new XElement("pets");
PetBehavior.SavePets(petsElement);
yield return CoroutineStatus.Running;
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
@@ -694,6 +705,10 @@ namespace Barotrauma
}
pendingHireInfos.Add(match);
if (pendingHireInfos.Count + CrewManager.CharacterInfos.Count() >= CrewManager.MaxCrewSize)
{
break;
}
}
location.HireManager.PendingHires = pendingHireInfos;
}
@@ -764,6 +779,11 @@ namespace Barotrauma
CargoManager?.SavePurchasedItems(modeElement);
UpgradeManager?.SavePendingUpgrades(modeElement, UpgradeManager?.PendingUpgrades);
if (petsElement != null)
{
modeElement.Add(petsElement);
}
// save bots
CrewManager.SaveMultiplayer(modeElement);

View File

@@ -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;
}

View File

@@ -131,7 +131,7 @@ namespace Barotrauma
case NetEntityEvent.Type.ChangeProperty:
try
{
WritePropertyChange(msg, extraData, false);
WritePropertyChange(msg, extraData, inGameEditableOnly: !GameMain.NetworkMember.IsServer);
}
catch (Exception e)
{
@@ -222,7 +222,7 @@ namespace Barotrauma
break;
case NetEntityEvent.Type.ChangeProperty:
ReadPropertyChange(msg, true, c);
ReadPropertyChange(msg, inGameEditableOnly: GameMain.NetworkMember.IsServer, sender: c);
break;
case NetEntityEvent.Type.Combine:
UInt16 combineTargetID = msg.ReadUInt16();
@@ -367,23 +367,42 @@ namespace Barotrauma
public void CreateServerEvent<T>(T ic) where T : ItemComponent, IServerSerializable
{
if (GameMain.Server == null) return;
if (GameMain.Server == null) { return; }
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;
}
int index = components.IndexOf(ic);
if (index == -1) return;
if (index == -1) { return; }
object[] extraData = new object[] { NetEntityEvent.Type.ComponentState, index };
ic.ServerAppendExtraData(ref extraData);
GameMain.Server.CreateEntityEvent(this, extraData);
}
public void CreateServerEvent<T>(T ic, object[] extraData) where T : ItemComponent, IServerSerializable
{
if (GameMain.Server == null) { return; }
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.CleanupStackTrace();
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Item.CreateServerEvent:EventForUninitializedItem" + Name + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
int index = components.IndexOf(ic);
if (index == -1) { return; }
object[] data = new object[] { NetEntityEvent.Type.ComponentState, index }.Concat(extraData).ToArray();
GameMain.Server.CreateEntityEvent(this, data);
}
}
}

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