(3a5d98b) v0.9.6.0
This commit is contained in:
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.9.5.1")]
|
||||
[assembly: AssemblyFileVersion("0.9.5.1")]
|
||||
[assembly: AssemblyVersion("0.9.6.0")]
|
||||
[assembly: AssemblyFileVersion("0.9.6.0")]
|
||||
|
||||
@@ -85,8 +85,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.Inventory != null)
|
||||
{
|
||||
if (!character.LockHands && character.Stun < 0.1f &&
|
||||
(character.SelectedConstruction == null || character.SelectedConstruction?.GetComponent<Controller>()?.User != character))
|
||||
if (!LockInventory(character))
|
||||
{
|
||||
character.Inventory.Update(deltaTime, cam);
|
||||
}
|
||||
@@ -325,7 +324,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (character.Inventory != null && !character.LockHands)
|
||||
{
|
||||
character.Inventory.Locked = (character.SelectedConstruction?.GetComponent<Controller>()?.User == character);
|
||||
character.Inventory.Locked = LockInventory(character);
|
||||
character.Inventory.DrawOwn(spriteBatch);
|
||||
character.Inventory.CurrentLayout = CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null ?
|
||||
CharacterInventory.Layout.Default :
|
||||
@@ -368,6 +367,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static bool LockInventory(Character character)
|
||||
{
|
||||
if (character?.Inventory == null || !character.AllowInput || character.LockHands) { return true; }
|
||||
|
||||
//lock if using a controller, except if we're also using a connection panel in the same item
|
||||
return
|
||||
character.SelectedConstruction != null &&
|
||||
character.SelectedConstruction?.GetComponent<Controller>()?.User == character &&
|
||||
character.SelectedConstruction?.GetComponent<ConnectionPanel>()?.User != character;
|
||||
}
|
||||
|
||||
private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order, float iconAlpha = 1.0f)
|
||||
{
|
||||
if (order.TargetAllCharacters)
|
||||
|
||||
@@ -2041,6 +2041,16 @@ namespace Barotrauma
|
||||
|
||||
commands.Add(new Command("spawnsub", "spawnsub [subname]: Spawn a submarine at the position of the cursor", (string[] args) =>
|
||||
{
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
ThrowError("Cannot spawn additional submarines during a multiplayer session.");
|
||||
return;
|
||||
}
|
||||
if (args.Length == 0)
|
||||
{
|
||||
ThrowError("Please enter the name of the submarine.");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
Submarine spawnedSub = Submarine.Load(args[0], false);
|
||||
|
||||
@@ -220,6 +220,7 @@ namespace Barotrauma
|
||||
msgHolder.RectTransform.Resize(new Point(msgHolder.Rect.Width, msgHolder.Children.Sum(c => c.Rect.Height) + (int)(10 * GUI.Scale)), resizeChildren: false);
|
||||
msgHolder.RectTransform.SizeChanged += Recalculate;
|
||||
chatBox.RecalculateChildren();
|
||||
chatBox.UpdateScrollBarSize();
|
||||
}
|
||||
|
||||
CoroutineManager.StartCoroutine(UpdateMessageAnimation(msgHolder, 0.5f));
|
||||
|
||||
@@ -435,12 +435,30 @@ namespace Barotrauma
|
||||
|
||||
public void SelectNext(bool force = false, bool autoScroll = true)
|
||||
{
|
||||
Select(Math.Min(Content.CountChildren - 1, SelectedIndex + 1), force, autoScroll);
|
||||
int index = SelectedIndex + 1;
|
||||
while (index < Content.CountChildren)
|
||||
{
|
||||
if (Content.GetChild(index).Visible)
|
||||
{
|
||||
Select(index, force, autoScroll);
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectPrevious(bool force = false, bool autoScroll = true)
|
||||
{
|
||||
Select(Math.Max(0, SelectedIndex - 1), force, autoScroll);
|
||||
int index = SelectedIndex - 1;
|
||||
while (index >= 0)
|
||||
{
|
||||
if (Content.GetChild(index).Visible)
|
||||
{
|
||||
Select(index, force, autoScroll);
|
||||
break;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
public void Select(int childIndex, bool force = false, bool autoScroll = true)
|
||||
|
||||
@@ -233,7 +233,7 @@ namespace Barotrauma
|
||||
switch (InputType)
|
||||
{
|
||||
case NumberType.Int:
|
||||
TextBox.textFilterFunction = text => new string(text.Where(c => char.IsNumber(c)).ToArray());
|
||||
TextBox.textFilterFunction = text => new string(text.Where(c => char.IsNumber(c) || c == '-').ToArray());
|
||||
break;
|
||||
case NumberType.Float:
|
||||
TextBox.textFilterFunction = text => new string(text.Where(c => char.IsDigit(c) || c == '.' || c == '-').ToArray());
|
||||
|
||||
@@ -126,6 +126,11 @@ namespace Barotrauma
|
||||
set { text.Text = value; }
|
||||
}
|
||||
|
||||
public Color? DefaultTextColor
|
||||
{
|
||||
get { return defaultTextColor; }
|
||||
}
|
||||
|
||||
public GUITickBox(RectTransform rectT, string label, ScalableFont font = null, string style = "") : base(null, rectT)
|
||||
{
|
||||
CanBeFocused = true;
|
||||
|
||||
@@ -318,6 +318,8 @@ namespace Barotrauma
|
||||
|
||||
private void InitUserStats()
|
||||
{
|
||||
return;
|
||||
|
||||
if (GameSettings.ShowUserStatisticsPrompt)
|
||||
{
|
||||
if (TextManager.ContainsTag("statisticspromptheader") && TextManager.ContainsTag("statisticsprompttext"))
|
||||
@@ -404,14 +406,18 @@ namespace Barotrauma
|
||||
GUI.Init(Window, Config.SelectedContentPackages, GraphicsDevice);
|
||||
DebugConsole.Init();
|
||||
|
||||
if (Config.AutoUpdateWorkshopItems)
|
||||
CrossThread.RequestExecutionOnMainThread(() =>
|
||||
{
|
||||
if (SteamManager.AutoUpdateWorkshopItems())
|
||||
if (Config.AutoUpdateWorkshopItems)
|
||||
{
|
||||
ContentPackage.LoadAll();
|
||||
Config.ReloadContentPackages();
|
||||
if (SteamManager.AutoUpdateWorkshopItems())
|
||||
{
|
||||
ContentPackage.LoadAll();
|
||||
Config.ReloadContentPackages();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (SelectedPackages.None())
|
||||
{
|
||||
@@ -844,6 +850,8 @@ namespace Barotrauma
|
||||
|
||||
SteamManager.Update((float)Timing.Step);
|
||||
|
||||
TaskPool.Update();
|
||||
|
||||
SoundManager?.Update();
|
||||
|
||||
Timing.Accumulator -= Timing.Step;
|
||||
|
||||
@@ -431,7 +431,6 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
characterArea.CanBeFocused = false;
|
||||
characterArea.CanBeSelected = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -204,9 +204,14 @@ namespace Barotrauma
|
||||
{
|
||||
campaign.SuppressStateSending = true;
|
||||
|
||||
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
|
||||
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
|
||||
campaign.Map.SelectMission(selectedMissionIndex);
|
||||
//we need to have the latest save file to display location/mission/store
|
||||
if (campaign.LastSaveID == saveID)
|
||||
{
|
||||
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
|
||||
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
|
||||
campaign.Map.SelectMission(selectedMissionIndex);
|
||||
campaign.CargoManager.SetPurchasedItems(purchasedItems);
|
||||
}
|
||||
|
||||
campaign.startWatchmanID = startWatchmanID;
|
||||
campaign.endWatchmanID = endWatchmanID;
|
||||
@@ -215,7 +220,6 @@ namespace Barotrauma
|
||||
campaign.PurchasedHullRepairs = purchasedHullRepairs;
|
||||
campaign.PurchasedItemRepairs = purchasedItemRepairs;
|
||||
campaign.PurchasedLostShuttles = purchasedLostShuttles;
|
||||
campaign.CargoManager.SetPurchasedItems(purchasedItems);
|
||||
|
||||
if (myCharacterInfo != null)
|
||||
{
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
|
||||
if (brokenSprite == null)
|
||||
{
|
||||
//broken doors turn black if no broken sprite has been configured
|
||||
color = color * (item.Condition / item.Prefab.Health);
|
||||
color *= (item.Condition / item.Prefab.Health);
|
||||
color.A = 255;
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage, bool forcedOpen)
|
||||
{
|
||||
if (isStuck ||
|
||||
if ((IsStuck && !isNetworkMessage) ||
|
||||
(PredictedState == null && isOpen == open) ||
|
||||
(PredictedState != null && isOpen == PredictedState.Value && isOpen == open))
|
||||
{
|
||||
@@ -210,11 +210,7 @@ namespace Barotrauma.Items.Components
|
||||
StopPicking(null);
|
||||
PlaySound(forcedOpen ? ActionType.OnPicked : ActionType.OnUse, item.WorldPosition);
|
||||
}
|
||||
}
|
||||
|
||||
//opening a partially stuck door makes it less stuck
|
||||
if (isOpen) stuck = MathHelper.Clamp(stuck - 30.0f, 0.0f, 100.0f);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
@@ -225,7 +221,7 @@ namespace Barotrauma.Items.Components
|
||||
bool forcedOpen = msg.ReadBoolean();
|
||||
SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen);
|
||||
Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||
|
||||
if (isStuck) { OpenState = 0.0f; }
|
||||
PredictedState = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,20 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
get { return light; }
|
||||
}
|
||||
|
||||
|
||||
public override void OnScaleChanged()
|
||||
{
|
||||
light.SpriteScale = Vector2.One * item.Scale;
|
||||
light.Position = ParentBody != null ? ParentBody.Position : item.Position;
|
||||
}
|
||||
|
||||
partial void SetLightSourceState(bool enabled, float brightness)
|
||||
{
|
||||
if (light == null) { return; }
|
||||
light.Enabled = enabled;
|
||||
light.Color = LightColor * brightness;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
{
|
||||
if (light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f)
|
||||
@@ -28,7 +41,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
if (light?.LightSprite != null && item.Prefab.CanSpriteFlipX)
|
||||
if (light?.LightSprite != null && item.Prefab.CanSpriteFlipX && item.body == null)
|
||||
{
|
||||
light.LightSpriteEffect = light.LightSpriteEffect == SpriteEffects.None ?
|
||||
SpriteEffects.FlipHorizontally : SpriteEffects.None;
|
||||
@@ -39,5 +52,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
IsOn = msg.ReadBoolean();
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
light.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,26 @@ namespace Barotrauma.Items.Components
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
state = msg.ReadBoolean();
|
||||
ushort userID = msg.ReadUInt16();
|
||||
if (userID == 0)
|
||||
{
|
||||
if (user != null)
|
||||
{
|
||||
IsActive = false;
|
||||
CancelUsing(user);
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Character newUser = Entity.FindEntityByID(userID) as Character;
|
||||
if (newUser != user)
|
||||
{
|
||||
CancelUsing(user);
|
||||
}
|
||||
user = newUser;
|
||||
IsActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,18 +23,23 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Enabled = false
|
||||
};
|
||||
powerIndicator.TextColor = powerIndicator.DefaultTextColor.Value;
|
||||
|
||||
highVoltageIndicator = new GUITickBox(new RectTransform(indicatorSize, paddedFrame.RectTransform) { AbsoluteOffset = new Point(0, (int)(40 * GUI.yScale)) },
|
||||
TextManager.Get("PowerTransferHighVoltage"), style: "IndicatorLightRed")
|
||||
{
|
||||
ToolTip = TextManager.Get("PowerTransferTipOvervoltage"),
|
||||
Enabled = false
|
||||
};
|
||||
highVoltageIndicator.TextColor = highVoltageIndicator.DefaultTextColor.Value;
|
||||
|
||||
lowVoltageIndicator = new GUITickBox(new RectTransform(indicatorSize, paddedFrame.RectTransform) { AbsoluteOffset = new Point(0, (int)(80 * GUI.yScale)) },
|
||||
TextManager.Get("PowerTransferLowVoltage"), style: "IndicatorLightRed")
|
||||
{
|
||||
ToolTip = TextManager.Get("PowerTransferTipLowvoltage"),
|
||||
Enabled = false
|
||||
};
|
||||
lowVoltageIndicator.TextColor = lowVoltageIndicator.DefaultTextColor.Value;
|
||||
|
||||
var textContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), paddedFrame.RectTransform, Anchor.TopRight));
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (repairSoundChannel == null || !repairSoundChannel.IsPlaying)
|
||||
{
|
||||
repairSoundChannel = SoundPlayer.PlaySound("repair", item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
repairSoundChannel = SoundPlayer.PlaySound("repair", item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -221,8 +221,17 @@ namespace Barotrauma.Items.Components
|
||||
deteriorationTimer = msg.ReadSingle();
|
||||
deteriorateAlwaysResetTimer = msg.ReadSingle();
|
||||
DeteriorateAlways = msg.ReadBoolean();
|
||||
CurrentFixer = msg.ReadBoolean() ? Character.Controlled : null;
|
||||
ushort currentFixerID = msg.ReadUInt16();
|
||||
currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
|
||||
|
||||
if (currentFixerID == 0)
|
||||
{
|
||||
CurrentFixer = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentFixer = Entity.FindEntityByID(currentFixerID) as Character;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
|
||||
@@ -119,6 +119,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
DrawWire(spriteBatch, draggingConnected, PlayerInput.MousePosition, new Vector2(x + width / 2, y + height - 10), null, panel, "");
|
||||
}
|
||||
panel.TriggerRewiringSound();
|
||||
|
||||
if (!PlayerInput.LeftButtonHeld())
|
||||
{
|
||||
@@ -129,7 +130,11 @@ namespace Barotrauma.Items.Components
|
||||
panel.DisconnectedWires.Add(draggingConnected);
|
||||
}
|
||||
|
||||
if (GameMain.Client != null) { panel.Item.CreateClientEvent(panel); }
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
panel.Item.CreateClientEvent(panel);
|
||||
}
|
||||
|
||||
draggingConnected = null;
|
||||
}
|
||||
}
|
||||
@@ -205,7 +210,10 @@ namespace Barotrauma.Items.Components
|
||||
SetWire(index, draggingConnected);
|
||||
}
|
||||
}
|
||||
if (GameMain.Client != null) { panel.Item.CreateClientEvent(panel); }
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
panel.Item.CreateClientEvent(panel);
|
||||
}
|
||||
draggingConnected = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,19 +11,28 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class ConnectionPanel : ItemComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
//how long the rewiring sound plays after doing changes to the wiring
|
||||
const float RewireSoundDuration = 5.0f;
|
||||
|
||||
public static Wire HighlightedWire;
|
||||
|
||||
private SoundChannel rewireSoundChannel;
|
||||
private float rewireSoundTimer;
|
||||
|
||||
partial void InitProjSpecific(XElement element)
|
||||
{
|
||||
if (GuiFrame == null) return;
|
||||
if (GuiFrame == null) { return; }
|
||||
new GUICustomComponent(new RectTransform(Vector2.One, GuiFrame.RectTransform), DrawConnections, null)
|
||||
{
|
||||
UserData = this
|
||||
};
|
||||
}
|
||||
|
||||
public void TriggerRewiringSound()
|
||||
{
|
||||
rewireSoundTimer = RewireSoundDuration;
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
{
|
||||
foreach (Wire wire in DisconnectedWires)
|
||||
@@ -40,7 +49,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
if (user != null && user.SelectedConstruction == item && HasRequiredItems(user, addMessage: false))
|
||||
|
||||
rewireSoundTimer -= deltaTime;
|
||||
if (user != null && user.SelectedConstruction == item && rewireSoundTimer > 0.0f)
|
||||
{
|
||||
if (rewireSoundChannel == null || !rewireSoundChannel.IsPlaying)
|
||||
{
|
||||
@@ -51,12 +62,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
rewireSoundChannel?.FadeOutAndDispose();
|
||||
rewireSoundChannel = null;
|
||||
rewireSoundTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
{
|
||||
if (item.Submarine == null || item.Submarine.Loading || Screen.Selected != GameMain.SubEditorScreen) return;
|
||||
if (item.Submarine == null || item.Submarine.Loading || Screen.Selected != GameMain.SubEditorScreen) { return; }
|
||||
MoveConnectedWires(amount);
|
||||
}
|
||||
|
||||
@@ -103,6 +115,7 @@ namespace Barotrauma.Items.Components
|
||||
//delay reading the state until midround syncing is done
|
||||
//because some of the wires connected to the panel may not exist yet
|
||||
long msgStartPos = msg.BitPosition;
|
||||
msg.ReadUInt16(); //user ID
|
||||
foreach (Connection connection in Connections)
|
||||
{
|
||||
for (int i = 0; i < Connection.MaxLinked; i++)
|
||||
@@ -121,6 +134,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
//don't trigger rewiring sounds if the rewiring is being done by the local user (in that case we'll trigger it locally)
|
||||
if (Character.Controlled == null || user != Character.Controlled) { TriggerRewiringSound(); }
|
||||
ApplyRemoteState(msg);
|
||||
}
|
||||
}
|
||||
@@ -130,6 +145,17 @@ namespace Barotrauma.Items.Components
|
||||
List<Wire> prevWires = Connections.SelectMany(c => c.Wires.Where(w => w != null)).ToList();
|
||||
List<Wire> newWires = new List<Wire>();
|
||||
|
||||
ushort userID = msg.ReadUInt16();
|
||||
|
||||
if (userID == 0)
|
||||
{
|
||||
user = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = Entity.FindEntityByID(userID) as Character;
|
||||
}
|
||||
|
||||
foreach (Connection connection in Connections)
|
||||
{
|
||||
connection.ClearConnections();
|
||||
|
||||
@@ -145,6 +145,21 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
Dock(DockingTarget);
|
||||
if (joint == null)
|
||||
{
|
||||
string errorMsg = "Error while reading a docking port network event (Dock method did not create a joint between the ports)." +
|
||||
" Submarine: " + (item.Submarine?.Name ?? "null") +
|
||||
", target submarine: " + (DockingTarget.item.Submarine?.Name ?? "null");
|
||||
if (item.Submarine?.DockedTo.Contains(DockingTarget.item.Submarine) ?? false)
|
||||
{
|
||||
errorMsg += "\nAlready docked.";
|
||||
}
|
||||
if (item.Submarine == DockingTarget.item.Submarine)
|
||||
{
|
||||
errorMsg += "\nTrying to dock the submarine to itself.";
|
||||
}
|
||||
GameAnalyticsManager.AddErrorEventOnce("DockingPort.ClientRead:JointNotCreated", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
|
||||
if (isLocked)
|
||||
{
|
||||
|
||||
@@ -916,21 +916,30 @@ namespace Barotrauma
|
||||
case NetEntityEvent.Type.ApplyStatusEffect:
|
||||
{
|
||||
ActionType actionType = (ActionType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length - 1);
|
||||
byte componentIndex = msg.ReadByte();
|
||||
ushort targetID = msg.ReadUInt16();
|
||||
byte targetLimbID = msg.ReadByte();
|
||||
byte componentIndex = msg.ReadByte();
|
||||
ushort targetCharacterID = msg.ReadUInt16();
|
||||
byte targetLimbID = msg.ReadByte();
|
||||
ushort useTargetID = msg.ReadUInt16();
|
||||
Vector2? worldPosition = null;
|
||||
bool hasPosition = msg.ReadBoolean();
|
||||
if (hasPosition)
|
||||
{
|
||||
worldPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
}
|
||||
|
||||
ItemComponent targetComponent = componentIndex < components.Count ? components[componentIndex] : null;
|
||||
Character target = FindEntityByID(targetID) as Character;
|
||||
Limb targetLimb = target != null && targetLimbID < target.AnimController.Limbs.Length ? target.AnimController.Limbs[targetLimbID] : null;
|
||||
|
||||
Character targetCharacter = FindEntityByID(targetCharacterID) as Character;
|
||||
Limb targetLimb = targetCharacter != null && targetLimbID < targetCharacter.AnimController.Limbs.Length ?
|
||||
targetCharacter.AnimController.Limbs[targetLimbID] : null;
|
||||
Entity useTarget = FindEntityByID(useTargetID);
|
||||
|
||||
if (targetComponent == null)
|
||||
{
|
||||
ApplyStatusEffects(actionType, 1.0f, target, targetLimb, true);
|
||||
ApplyStatusEffects(actionType, 1.0f, targetCharacter, targetLimb, useTarget, true, worldPosition: worldPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetComponent.ApplyStatusEffects(actionType, 1.0f, target, targetLimb);
|
||||
targetComponent.ApplyStatusEffects(actionType, 1.0f, targetCharacter, targetLimb, useTarget, worldPosition: worldPosition);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -228,12 +228,12 @@ namespace Barotrauma.Lights
|
||||
activeLights.Clear();
|
||||
foreach (LightSource light in lights)
|
||||
{
|
||||
if (light.Color.A < 1 || light.Range < 1.0f || !light.Enabled) continue;
|
||||
if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.Range, viewRect)) continue;
|
||||
if (!light.Enabled) { continue; }
|
||||
if ((light.Color.A < 1 || light.Range < 1.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; }
|
||||
if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.LightSourceParams.TextureRange, viewRect)) { continue; }
|
||||
activeLights.Add(light);
|
||||
}
|
||||
|
||||
|
||||
//clear the lightmap
|
||||
graphics.Clear(Color.Black);
|
||||
graphics.BlendState = BlendState.Additive;
|
||||
@@ -244,9 +244,9 @@ namespace Barotrauma.Lights
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
|
||||
foreach (LightSource light in activeLights)
|
||||
{
|
||||
if (!light.IsBackground) continue;
|
||||
if (!light.IsBackground) { continue; }
|
||||
light.DrawSprite(spriteBatch, cam);
|
||||
light.DrawLightVolume(spriteBatch, lightEffect, transform);
|
||||
if (light.Color.A > 0 && light.Range > 0.0f) { light.DrawLightVolume(spriteBatch, lightEffect, transform); }
|
||||
backgroundSpritesDrawn = true;
|
||||
}
|
||||
GameMain.ParticleManager.Draw(spriteBatch, true, null, Particles.ParticleBlendState.Additive);
|
||||
@@ -288,7 +288,7 @@ namespace Barotrauma.Lights
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
|
||||
foreach (LightSource light in activeLights)
|
||||
{
|
||||
if (light.IsBackground) continue;
|
||||
if (light.IsBackground) { continue; }
|
||||
light.DrawSprite(spriteBatch, cam);
|
||||
}
|
||||
spriteBatch.End();
|
||||
@@ -303,6 +303,8 @@ namespace Barotrauma.Lights
|
||||
//draw characters to obstruct the highlighted items/characters and light sprites
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"];
|
||||
SolidColorEffect.Parameters["color"].SetValue(Color.Black.ToVector4());
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
@@ -347,8 +349,8 @@ namespace Barotrauma.Lights
|
||||
|
||||
foreach (LightSource light in activeLights)
|
||||
{
|
||||
if (light.IsBackground) continue;
|
||||
light.DrawLightVolume(spriteBatch, lightEffect, transform);
|
||||
if (light.IsBackground) { continue; }
|
||||
if (light.Color.A > 0 && light.Range > 0.0f) { light.DrawLightVolume(spriteBatch, lightEffect, transform); }
|
||||
}
|
||||
Vector3 offset = Vector3.Zero;// new Vector3(Submarine.MainSub.DrawPosition.X, Submarine.MainSub.DrawPosition.Y, 0.0f);
|
||||
lightEffect.World = Matrix.CreateTranslation(Vector3.Zero) * transform;
|
||||
|
||||
@@ -31,10 +31,22 @@ namespace Barotrauma.Lights
|
||||
get { return range; }
|
||||
set
|
||||
{
|
||||
|
||||
range = MathHelper.Clamp(value, 0.0f, 2048.0f);
|
||||
TextureRange = range;
|
||||
if (OverrideLightTexture != null)
|
||||
{
|
||||
TextureRange += Math.Max(
|
||||
Math.Abs(OverrideLightTexture.RelativeOrigin.X - 0.5f) * OverrideLightTexture.size.X,
|
||||
Math.Abs(OverrideLightTexture.RelativeOrigin.Y - 0.5f) * OverrideLightTexture.size.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float TextureRange
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Sprite OverrideLightTexture
|
||||
{
|
||||
@@ -89,6 +101,8 @@ namespace Barotrauma.Lights
|
||||
break;
|
||||
case "lighttexture":
|
||||
OverrideLightTexture = new Sprite(subElement, preMultiplyAlpha: false);
|
||||
//refresh TextureRange
|
||||
Range = range;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -115,8 +129,16 @@ namespace Barotrauma.Lights
|
||||
|
||||
class LightSource
|
||||
{
|
||||
//how many pixels the position of the light needs to change for the light volume to be recalculated
|
||||
const float MovementRecalculationThreshold = 10.0f;
|
||||
//how many radians the light needs to rotate for the light volume to be recalculated
|
||||
const float RotationRecalculationThreshold = 0.02f;
|
||||
|
||||
private static Texture2D lightTexture;
|
||||
|
||||
private VertexPositionColorTexture[] vertices;
|
||||
private short[] indices;
|
||||
|
||||
private List<ConvexHullList> hullsInRange;
|
||||
|
||||
public Texture2D texture;
|
||||
@@ -167,6 +189,9 @@ namespace Barotrauma.Lights
|
||||
private int vertexCount;
|
||||
private int indexCount;
|
||||
|
||||
private Vector2 translateVertices;
|
||||
private float rotateVertices;
|
||||
|
||||
private readonly LightSourceParams lightSourceParams;
|
||||
|
||||
public LightSourceParams LightSourceParams => lightSourceParams;
|
||||
@@ -177,26 +202,38 @@ namespace Barotrauma.Lights
|
||||
get { return position; }
|
||||
set
|
||||
{
|
||||
if (Math.Abs(position.X - value.X) < 0.1f && Math.Abs(position.Y - value.Y) < 0.1f) return;
|
||||
Vector2 moveAmount = value - position;
|
||||
if (Math.Abs(moveAmount.X) < 0.1f && Math.Abs(moveAmount.Y) < 0.1f) { return; }
|
||||
position = value;
|
||||
|
||||
if (Vector2.DistanceSquared(prevCalculatedPosition, position) < 5.0f * 5.0f) return;
|
||||
//translate light volume manually instead of doing a full recalculation when moving by a small amount
|
||||
if (Vector2.DistanceSquared(prevCalculatedPosition, position) < MovementRecalculationThreshold * MovementRecalculationThreshold && vertices != null)
|
||||
{
|
||||
translateVertices = position - prevCalculatedPosition;
|
||||
return;
|
||||
}
|
||||
|
||||
NeedsHullCheck = true;
|
||||
NeedsRecalculation = true;
|
||||
prevCalculatedPosition = position;
|
||||
}
|
||||
}
|
||||
|
||||
private float prevCalculatedRotation;
|
||||
private float rotation;
|
||||
public float Rotation
|
||||
{
|
||||
get { return rotation; }
|
||||
set
|
||||
{
|
||||
if (Math.Abs(rotation - value) < 0.01f) return;
|
||||
if (Math.Abs(value - rotation) < 0.001f) { return; }
|
||||
rotation = value;
|
||||
|
||||
if (Math.Abs(rotation - prevCalculatedRotation) < RotationRecalculationThreshold && vertices != null)
|
||||
{
|
||||
rotateVertices = rotation - prevCalculatedRotation;
|
||||
return;
|
||||
}
|
||||
|
||||
NeedsHullCheck = true;
|
||||
NeedsRecalculation = true;
|
||||
}
|
||||
@@ -711,16 +748,25 @@ namespace Barotrauma.Lights
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
private void CalculateLightVertices(List<Vector2> rayCastHits)
|
||||
{
|
||||
List<VertexPositionColorTexture> vertices = new List<VertexPositionColorTexture>();
|
||||
vertexCount = rayCastHits.Count * 2 + 1;
|
||||
indexCount = (rayCastHits.Count) * 9;
|
||||
|
||||
//recreate arrays if they're too small or excessively large
|
||||
if (vertices == null || vertices.Length < vertexCount || vertices.Length > vertexCount * 3)
|
||||
{
|
||||
vertices = new VertexPositionColorTexture[vertexCount];
|
||||
indices = new short[indexCount];
|
||||
}
|
||||
|
||||
Vector2 drawPos = position;
|
||||
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
|
||||
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
|
||||
|
||||
float cosAngle = (float)Math.Cos(Rotation);
|
||||
float sinAngle = -(float)Math.Sin(Rotation);
|
||||
|
||||
|
||||
Vector2 uvOffset = Vector2.Zero;
|
||||
Vector2 overrideTextureDims = Vector2.One;
|
||||
if (OverrideLightTexture != null)
|
||||
@@ -728,15 +774,15 @@ namespace Barotrauma.Lights
|
||||
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
|
||||
|
||||
Vector2 origin = OverrideLightTexture.Origin;
|
||||
if (LightSpriteEffect == SpriteEffects.FlipHorizontally) origin.X = OverrideLightTexture.SourceRect.Width - origin.X;
|
||||
if (LightSpriteEffect == SpriteEffects.FlipVertically) origin.Y = (OverrideLightTexture.SourceRect.Height - origin.Y);
|
||||
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);
|
||||
}
|
||||
|
||||
// Add a vertex for the center of the mesh
|
||||
vertices.Add(new VertexPositionColorTexture(new Vector3(position.X, position.Y, 0),
|
||||
Color.White, new Vector2(0.5f, 0.5f) + uvOffset));
|
||||
|
||||
vertices[0] = new VertexPositionColorTexture(new Vector3(position.X, position.Y, 0),
|
||||
Color.White, GetUV(new Vector2(0.5f, 0.5f) + uvOffset, LightSpriteEffect));
|
||||
|
||||
//hacky fix to exc excessively large light volumes (they used to be up to 4x the range of the light if there was nothing to block the rays).
|
||||
//might want to tweak the raycast logic in a way that this isn't necessary
|
||||
float boundRadius = Range * 1.1f / (1.0f - Math.Max(Math.Abs(uvOffset.X), Math.Abs(uvOffset.Y)));
|
||||
@@ -800,68 +846,88 @@ namespace Barotrauma.Lights
|
||||
|
||||
//finally, create the vertices
|
||||
VertexPositionColorTexture fullVert = new VertexPositionColorTexture(new Vector3(position.X + rawDiff.X, position.Y + rawDiff.Y, 0),
|
||||
Color.White, new Vector2(0.5f, 0.5f) + diff);
|
||||
Color.White, GetUV(new Vector2(0.5f, 0.5f) + diff, LightSpriteEffect));
|
||||
VertexPositionColorTexture fadeVert = new VertexPositionColorTexture(new Vector3(position.X + rawDiff.X + nDiff.X, position.Y + rawDiff.Y + nDiff.Y, 0),
|
||||
Color.White * 0.0f, new Vector2(0.5f, 0.5f) + diff);
|
||||
Color.White * 0.0f, GetUV(new Vector2(0.5f, 0.5f) + diff, LightSpriteEffect));
|
||||
|
||||
vertices.Add(fullVert);
|
||||
vertices.Add(fadeVert);
|
||||
vertices[1 + i * 2] = fullVert;
|
||||
vertices[1 + i * 2 + 1] = fadeVert;
|
||||
}
|
||||
|
||||
// Compute the indices to form triangles
|
||||
List<short> indices = new List<short>();
|
||||
for (int i = 0; i < rayCastHits.Count-1; i++)
|
||||
for (int i = 0; i < rayCastHits.Count - 1; i++)
|
||||
{
|
||||
//main light body
|
||||
indices.Add(0);
|
||||
indices.Add((short)((i*2 + 3) % vertices.Count));
|
||||
indices.Add((short)((i*2 + 1) % vertices.Count));
|
||||
|
||||
indices[i * 9] = 0;
|
||||
indices[i * 9 + 1] = (short)((i * 2 + 3) % vertexCount);
|
||||
indices[i * 9 + 2] = (short)((i * 2 + 1) % vertexCount);
|
||||
|
||||
//faded light
|
||||
indices.Add((short)((i*2 + 1) % vertices.Count));
|
||||
indices.Add((short)((i*2 + 3) % vertices.Count));
|
||||
indices.Add((short)((i*2 + 4) % vertices.Count));
|
||||
indices[i * 9 + 3] = (short)((i * 2 + 1) % vertexCount);
|
||||
indices[i * 9 + 4] = (short)((i * 2 + 3) % vertexCount);
|
||||
indices[i * 9 + 5] = (short)((i * 2 + 4) % vertexCount);
|
||||
|
||||
indices.Add((short)((i*2 + 2) % vertices.Count));
|
||||
indices.Add((short)((i*2 + 1) % vertices.Count));
|
||||
indices.Add((short)((i*2 + 4) % vertices.Count));
|
||||
indices[i * 9 + 6] = (short)((i * 2 + 2) % vertexCount);
|
||||
indices[i * 9 + 7] = (short)((i * 2 + 1) % vertexCount);
|
||||
indices[i * 9 + 8] = (short)((i * 2 + 4) % vertexCount);
|
||||
}
|
||||
|
||||
|
||||
//main light body
|
||||
indices.Add(0);
|
||||
indices.Add((short)(1));
|
||||
indices.Add((short)(vertices.Count - 2));
|
||||
|
||||
indices[(rayCastHits.Count - 1) * 9] = 0;
|
||||
indices[(rayCastHits.Count - 1) * 9 + 1] = (short)(1);
|
||||
indices[(rayCastHits.Count - 1) * 9 + 2] = (short)(vertexCount - 2);
|
||||
|
||||
//faded light
|
||||
indices.Add((short)(1));
|
||||
indices.Add((short)(vertices.Count-1));
|
||||
indices.Add((short)(vertices.Count-2));
|
||||
indices[(rayCastHits.Count - 1) * 9 + 3] = (short)(1);
|
||||
indices[(rayCastHits.Count - 1) * 9 + 4] = (short)(vertexCount - 1);
|
||||
indices[(rayCastHits.Count - 1) * 9 + 5] = (short)(vertexCount - 2);
|
||||
|
||||
indices.Add((short)(1));
|
||||
indices.Add((short)(2));
|
||||
indices.Add((short)(vertices.Count-1));
|
||||
|
||||
vertexCount = vertices.Count;
|
||||
indexCount = indices.Count;
|
||||
indices[(rayCastHits.Count - 1) * 9 + 6] = (short)(1);
|
||||
indices[(rayCastHits.Count - 1) * 9 + 7] = (short)(2);
|
||||
indices[(rayCastHits.Count - 1) * 9 + 8] = (short)(vertexCount - 1);
|
||||
|
||||
//TODO: a better way to determine the size of the vertex buffer and handle changes in size?
|
||||
//now we just create a buffer for 64 verts and make it larger if needed
|
||||
if (lightVolumeBuffer == null)
|
||||
{
|
||||
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, Math.Max(64, (int)(vertexCount*1.5)), BufferUsage.None);
|
||||
lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.Instance.GraphicsDevice, typeof(short), Math.Max(64*3, (int)(indexCount * 1.5)), BufferUsage.None);
|
||||
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, Math.Max(64, (int)(vertexCount * 1.5)), BufferUsage.None);
|
||||
lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.Instance.GraphicsDevice, typeof(short), Math.Max(64 * 3, (int)(indexCount * 1.5)), BufferUsage.None);
|
||||
}
|
||||
else if (vertexCount > lightVolumeBuffer.VertexCount || indexCount > lightVolumeIndexBuffer.IndexCount)
|
||||
{
|
||||
lightVolumeBuffer.Dispose();
|
||||
lightVolumeIndexBuffer.Dispose();
|
||||
|
||||
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, (int)(vertexCount*1.5), BufferUsage.None);
|
||||
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, (int)(vertexCount * 1.5), BufferUsage.None);
|
||||
lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.Instance.GraphicsDevice, typeof(short), (int)(indexCount * 1.5), BufferUsage.None);
|
||||
}
|
||||
|
||||
lightVolumeBuffer.SetData<VertexPositionColorTexture>(vertices.ToArray());
|
||||
lightVolumeIndexBuffer.SetData<short>(indices.ToArray());
|
||||
|
||||
lightVolumeBuffer.SetData<VertexPositionColorTexture>(vertices, 0, vertexCount);
|
||||
lightVolumeIndexBuffer.SetData<short>(indices, 0, indexCount);
|
||||
|
||||
Vector2 GetUV(Vector2 vert, SpriteEffects effects)
|
||||
{
|
||||
if (effects == SpriteEffects.FlipHorizontally)
|
||||
{
|
||||
vert.X = 1.0f - vert.X;
|
||||
}
|
||||
else if (effects == SpriteEffects.FlipVertically)
|
||||
{
|
||||
vert.Y = 1.0f - vert.Y;
|
||||
}
|
||||
else if (effects == (SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically))
|
||||
{
|
||||
vert.X = 1.0f - vert.X;
|
||||
vert.Y = 1.0f - vert.Y;
|
||||
}
|
||||
vert.Y = 1.0f - vert.Y;
|
||||
return vert;
|
||||
}
|
||||
|
||||
translateVertices = Vector2.Zero;
|
||||
rotateVertices = 0.0f;
|
||||
prevCalculatedPosition = position;
|
||||
prevCalculatedRotation = rotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -944,15 +1010,12 @@ namespace Barotrauma.Lights
|
||||
|
||||
Vector2 drawPos = position;
|
||||
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
|
||||
drawPos.Y = -drawPos.Y;
|
||||
drawPos.Y = -drawPos.Y;
|
||||
|
||||
spriteBatch.Draw(currentTexture, drawPos, null, Color, -rotation, center, scale, SpriteEffects.None, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 offset = ParentSub == null ?
|
||||
Vector3.Zero : new Vector3(ParentSub.DrawPosition.X, ParentSub.DrawPosition.Y, 0.0f);
|
||||
lightEffect.World = Matrix.CreateTranslation(offset) * transform;
|
||||
|
||||
if (NeedsRecalculation)
|
||||
{
|
||||
@@ -962,8 +1025,16 @@ namespace Barotrauma.Lights
|
||||
lastRecalculationTime = (float)Timing.TotalTime;
|
||||
NeedsRecalculation = false;
|
||||
}
|
||||
|
||||
if (vertexCount == 0) return;
|
||||
|
||||
|
||||
Vector2 offset = ParentSub == null ? Vector2.Zero : ParentSub.DrawPosition;
|
||||
lightEffect.World =
|
||||
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
|
||||
Matrix.CreateRotationZ(rotateVertices) *
|
||||
Matrix.CreateTranslation(new Vector3(position + offset + translateVertices, 0.0f)) *
|
||||
transform;
|
||||
|
||||
if (vertexCount == 0) { return; }
|
||||
|
||||
lightEffect.DiffuseColor = (new Vector3(Color.R, Color.G, Color.B) * (Color.A / 255.0f)) / 255.0f;
|
||||
if (OverrideLightTexture != null)
|
||||
|
||||
@@ -483,6 +483,8 @@ namespace Barotrauma.Networking
|
||||
if (DateTime.Now > timeOut)
|
||||
{
|
||||
clientPeer?.Close(Lidgren.Network.NetConnection.NoResponseMessage);
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionFailed"), TextManager.Get("CouldNotConnectToServer"));
|
||||
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
|
||||
reconnectBox?.Close(); reconnectBox = null;
|
||||
break;
|
||||
}
|
||||
@@ -1232,7 +1234,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (Level.Loaded.EqualityCheckVal != levelEqualityCheckVal)
|
||||
{
|
||||
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ").";
|
||||
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed: " + Level.Loaded.Seed +
|
||||
", sub: " + Submarine.MainSub.Name + " (" + Submarine.MainSub.MD5Hash.ShortHash + ")" +
|
||||
", mirrored: " + Level.Loaded.Mirrored + ").";
|
||||
DebugConsole.ThrowError(errorMsg, createMessageBox: true);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
CoroutineManager.StartCoroutine(EndGame(""));
|
||||
@@ -1274,6 +1278,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
gameStarted = false;
|
||||
Character.Controlled = null;
|
||||
SpawnAsTraitor = false;
|
||||
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
|
||||
GameMain.LightManager.LosEnabled = false;
|
||||
respawnManager = null;
|
||||
@@ -1432,6 +1437,8 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private bool initialUpdateReceived;
|
||||
|
||||
private void ReadLobbyUpdate(IReadMessage inc)
|
||||
{
|
||||
ServerNetObject objHeader;
|
||||
@@ -1452,13 +1459,15 @@ namespace Barotrauma.Networking
|
||||
UInt16 settingsLen = inc.ReadUInt16();
|
||||
byte[] settingsData = inc.ReadBytes(settingsLen);
|
||||
|
||||
if (inc.ReadBoolean())
|
||||
bool isInitialUpdate = inc.ReadBoolean();
|
||||
if (isInitialUpdate)
|
||||
{
|
||||
if (GameSettings.VerboseLogging)
|
||||
{
|
||||
DebugConsole.NewMessage("Received initial lobby update, ID: " + updateID + ", last ID: " + GameMain.NetLobbyScreen.LastUpdateID, Color.Gray);
|
||||
}
|
||||
ReadInitialUpdate(inc);
|
||||
initialUpdateReceived = true;
|
||||
}
|
||||
|
||||
string selectSubName = inc.ReadString();
|
||||
@@ -1489,7 +1498,9 @@ namespace Barotrauma.Networking
|
||||
float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f;
|
||||
|
||||
//ignore the message if we already a more up-to-date one
|
||||
if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID))
|
||||
//or if we're still waiting for the initial update
|
||||
if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) &&
|
||||
(isInitialUpdate || initialUpdateReceived))
|
||||
{
|
||||
ReadWriteMessage settingsBuf = new ReadWriteMessage();
|
||||
settingsBuf.Write(settingsData, 0, settingsLen); settingsBuf.BitPosition = 0;
|
||||
@@ -2248,12 +2259,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (gameStarted)
|
||||
{
|
||||
tickBox.Visible = false;
|
||||
tickBox.Parent.Visible = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Vote(VoteType.StartRound, tickBox.Selected);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -200,6 +200,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
msg.BitPosition += msgLength * 8;
|
||||
msg.ReadPadBits();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -213,6 +214,20 @@ namespace Barotrauma.Networking
|
||||
try
|
||||
{
|
||||
ReadEvent(msg, entity, sendingTime);
|
||||
msg.ReadPadBits();
|
||||
|
||||
if (msg.BitPosition != msgPosition + msgLength * 8)
|
||||
{
|
||||
string errorMsg = "Message byte position incorrect after reading an event for the entity \"" + entity.ToString()
|
||||
+ "\". Read " + (msg.BitPosition - msgPosition) + " bits, expected message length was " + (msgLength * 8) + " bits.";
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#endif
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
|
||||
//TODO: force the BitPosition to correct place? Having some entity in a potentially incorrect state is not as bad as a desync kick
|
||||
//msg.BitPosition = (int)(msgPosition + msgLength * 8);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
@@ -231,9 +246,9 @@ namespace Barotrauma.Networking
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
msg.BitPosition = (int)(msgPosition + msgLength * 8);
|
||||
msg.ReadPadBits();
|
||||
}
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -275,6 +275,7 @@ namespace Barotrauma.Networking
|
||||
#if DEBUG
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
{
|
||||
if (GameMain.Client == null) { return; }
|
||||
if (Rand.Range(0.0f, 1.0f) < GameMain.Client.SimulatedLoss && sendType != Facepunch.Steamworks.Networking.SendType.Reliable) { return; }
|
||||
int count = Rand.Range(0.0f, 1.0f) < GameMain.Client.SimulatedDuplicatesChance ? 2 : 1;
|
||||
for (int i = 0; i < count; i++)
|
||||
|
||||
@@ -18,7 +18,17 @@ namespace Barotrauma.Networking
|
||||
public UInt64 OwnerID;
|
||||
public bool OwnerVerified;
|
||||
|
||||
public string ServerName;
|
||||
private string serverName;
|
||||
public string ServerName
|
||||
{
|
||||
get { return serverName; }
|
||||
set
|
||||
{
|
||||
serverName = value;
|
||||
if (serverName.Length > NetConfig.ServerNameMaxLength) { ServerName = ServerName.Substring(0, NetConfig.ServerNameMaxLength); }
|
||||
}
|
||||
}
|
||||
|
||||
public string ServerMessage;
|
||||
public bool GameStarted;
|
||||
public int PlayerCount;
|
||||
@@ -455,38 +465,7 @@ namespace Barotrauma.Networking
|
||||
if (SteamFriend.IsPlayingThisGame && SteamFriend.ServerLobbyId != 0)
|
||||
{
|
||||
LobbyID = SteamFriend.ServerLobbyId;
|
||||
SteamManager.Instance.LobbyList.SetManualLobbyDataCallback(LobbyID, (lobby) =>
|
||||
{
|
||||
SteamManager.Instance.LobbyList.SetManualLobbyDataCallback(LobbyID, null);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(lobby.GetData("haspassword"))) { return; }
|
||||
bool.TryParse(lobby.GetData("haspassword"), out bool hasPassword);
|
||||
int.TryParse(lobby.GetData("playercount"), out int currPlayers);
|
||||
int.TryParse(lobby.GetData("maxplayernum"), out int maxPlayers);
|
||||
//UInt64.TryParse(lobby.GetData("connectsteamid"), out ulong connectSteamId);
|
||||
string ip = lobby.GetData("hostipaddress");
|
||||
UInt64 ownerId = SteamManager.SteamIDStringToUInt64(lobby.GetData("lobbyowner"));
|
||||
|
||||
if (OwnerID != ownerId) { return; }
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ip)) { ip = ""; }
|
||||
|
||||
ServerName = lobby.Name;
|
||||
Port = "";
|
||||
QueryPort = "";
|
||||
IP = ip;
|
||||
PlayerCount = currPlayers;
|
||||
MaxPlayers = maxPlayers;
|
||||
HasPassword = hasPassword;
|
||||
RespondedToSteamQuery = true;
|
||||
LobbyID = lobby.LobbyID;
|
||||
OwnerID = ownerId;
|
||||
PingChecked = false;
|
||||
OwnerVerified = true;
|
||||
SteamManager.AssignLobbyDataToServerInfo(lobby, this);
|
||||
|
||||
onServerRulesReceived?.Invoke(this);
|
||||
});
|
||||
|
||||
SteamManager.Instance.LobbyList.RequestLobbyData(LobbyID);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -326,8 +326,8 @@ namespace Barotrauma.Steam
|
||||
localQuery.OnFinished = onFinished;
|
||||
#endif
|
||||
|
||||
instance.client.LobbyList.OnLobbiesUpdated = () => { UpdateLobbyQuery(onServerFound, onServerRulesReceived, onFinished); };
|
||||
instance.client.LobbyList.Refresh();
|
||||
|
||||
instance.client.LobbyList.Request();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -382,42 +382,6 @@ namespace Barotrauma.Steam
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void UpdateLobbyQuery(Action<Networking.ServerInfo> onServerFound, Action<Networking.ServerInfo> onServerRulesReceived, Action onFinished)
|
||||
{
|
||||
foreach (LobbyList.Lobby lobby in instance.client.LobbyList.Lobbies)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(lobby.GetData("haspassword"))) { continue; }
|
||||
bool.TryParse(lobby.GetData("haspassword"), out bool hasPassword);
|
||||
int.TryParse(lobby.GetData("playercount"), out int currPlayers);
|
||||
int.TryParse(lobby.GetData("maxplayernum"), out int maxPlayers);
|
||||
UInt64 ownerId = SteamIDStringToUInt64(lobby.GetData("lobbyowner"));
|
||||
//UInt64.TryParse(lobby.GetData("connectsteamid"), out ulong connectSteamId);
|
||||
string ip = lobby.GetData("hostipaddress");
|
||||
if (string.IsNullOrWhiteSpace(ip)) { ip = ""; }
|
||||
|
||||
var serverInfo = new ServerInfo()
|
||||
{
|
||||
ServerName = lobby.Name,
|
||||
Port = "",
|
||||
QueryPort = "",
|
||||
IP = ip,
|
||||
PlayerCount = currPlayers,
|
||||
MaxPlayers = maxPlayers,
|
||||
HasPassword = hasPassword,
|
||||
RespondedToSteamQuery = true,
|
||||
LobbyID = lobby.LobbyID,
|
||||
OwnerID = ownerId
|
||||
};
|
||||
serverInfo.PingChecked = false;
|
||||
AssignLobbyDataToServerInfo(lobby, serverInfo);
|
||||
|
||||
onServerFound(serverInfo);
|
||||
//onServerRulesReceived(serverInfo);
|
||||
}
|
||||
|
||||
onFinished();
|
||||
}
|
||||
|
||||
public static void AssignLobbyDataToServerInfo(LobbyList.Lobby lobby, ServerInfo serverInfo)
|
||||
{
|
||||
serverInfo.ServerMessage = lobby.GetData("message");
|
||||
@@ -495,6 +459,7 @@ namespace Barotrauma.Steam
|
||||
serverInfo.PingChecked = true;
|
||||
serverInfo.Ping = s.Ping;
|
||||
serverInfo.LobbyID = 0;
|
||||
serverInfo.OwnerVerified = true;
|
||||
if (responded)
|
||||
{
|
||||
s.FetchRules();
|
||||
|
||||
@@ -248,8 +248,10 @@ namespace Barotrauma
|
||||
MapEntityCategory newCategory = (MapEntityCategory)userdata;
|
||||
if (newCategory != selectedItemCategory)
|
||||
{
|
||||
searchBox.Text = "";
|
||||
searchBox.Text = "";
|
||||
storeItemList.ScrollBar.BarScroll = 0f;
|
||||
}
|
||||
|
||||
FilterStoreItems((MapEntityCategory)userdata, searchBox.Text);
|
||||
return true;
|
||||
}
|
||||
@@ -947,7 +949,9 @@ namespace Barotrauma
|
||||
var itemFrame = myItemList.Content.GetChildByUserData(pi);
|
||||
if (itemFrame == null)
|
||||
{
|
||||
itemFrame = CreateItemFrame(pi, pi.ItemPrefab.GetPrice(Campaign.Map.CurrentLocation), myItemList);
|
||||
var priceInfo = pi.ItemPrefab.GetPrice(Campaign.Map.CurrentLocation);
|
||||
if (priceInfo == null) { continue; }
|
||||
itemFrame = CreateItemFrame(pi, priceInfo, myItemList);
|
||||
}
|
||||
itemFrame.GetChild(0).GetChild<GUINumberInput>().IntValue = pi.Quantity;
|
||||
existingItemFrames.Add(itemFrame);
|
||||
|
||||
@@ -99,6 +99,7 @@ namespace Barotrauma.CharacterEditor
|
||||
private Rectangle spriteSheetRect;
|
||||
|
||||
private Rectangle CalculateSpritesheetRectangle() =>
|
||||
Textures == null || Textures.None() ? Rectangle.Empty :
|
||||
new Rectangle(
|
||||
spriteSheetOffsetX,
|
||||
spriteSheetOffsetY,
|
||||
@@ -656,12 +657,6 @@ namespace Barotrauma.CharacterEditor
|
||||
}
|
||||
if (!isFrozen)
|
||||
{
|
||||
if (character.AnimController.Invalid)
|
||||
{
|
||||
Reset(new Character[] { character });
|
||||
SpawnCharacter(currentCharacterConfig);
|
||||
}
|
||||
|
||||
Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position);
|
||||
Submarine.MainSub.Update((float)deltaTime);
|
||||
|
||||
@@ -722,6 +717,7 @@ namespace Barotrauma.CharacterEditor
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
if (limb == null || limb.ActiveSprite == null) { continue; }
|
||||
if (selectedJoints.Any(j => j.LimbA == limb || j.LimbB == limb)) { continue; }
|
||||
// Select limbs on ragdoll
|
||||
if (editLimbs && !spriteSheetRect.Contains(PlayerInput.MousePosition) && MathUtils.RectangleContainsPoint(GetLimbPhysicRect(limb), PlayerInput.MousePosition))
|
||||
{
|
||||
@@ -2727,11 +2723,19 @@ namespace Barotrauma.CharacterEditor
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
character.Params.Save();
|
||||
GUI.AddMessage(GetCharacterEditorTranslation("CharacterSavedTo").Replace("[path]", CharacterParams.FullPath), Color.Green, font: GUI.Font, lifeTime: 5);
|
||||
character.AnimController.SaveRagdoll();
|
||||
GUI.AddMessage(GetCharacterEditorTranslation("RagdollSavedTo").Replace("[path]", RagdollParams.FullPath), Color.Green, font: GUI.Font, lifeTime: 5);
|
||||
AnimParams.ForEach(p => p.Save());
|
||||
if (!string.IsNullOrEmpty(RagdollParams.Texture) && !File.Exists(RagdollParams.Texture))
|
||||
{
|
||||
DebugConsole.ThrowError($"Invalid texture path: {RagdollParams.Texture}");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
character.Params.Save();
|
||||
GUI.AddMessage(GetCharacterEditorTranslation("CharacterSavedTo").Replace("[path]", CharacterParams.FullPath), Color.Green, font: GUI.Font, lifeTime: 5);
|
||||
character.AnimController.SaveRagdoll();
|
||||
GUI.AddMessage(GetCharacterEditorTranslation("RagdollSavedTo").Replace("[path]", RagdollParams.FullPath), Color.Green, font: GUI.Font, lifeTime: 5);
|
||||
AnimParams.ForEach(p => p.Save());
|
||||
}
|
||||
return true;
|
||||
};
|
||||
// Spacing
|
||||
|
||||
@@ -40,6 +40,10 @@ namespace Barotrauma.CharacterEditor
|
||||
canEnterSubmarine = ragdoll.CanEnterSubmarine;
|
||||
canWalk = ragdoll.CanWalk;
|
||||
texturePath = ragdoll.Texture;
|
||||
if (string.IsNullOrEmpty(texturePath) && !name.Equals(Character.HumanSpeciesName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
texturePath = ragdoll.Limbs.FirstOrDefault()?.GetSprite().Texture;
|
||||
}
|
||||
}
|
||||
|
||||
public static Wizard instance;
|
||||
@@ -265,8 +269,30 @@ namespace Barotrauma.CharacterEditor
|
||||
};
|
||||
if (ofd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
string file = ofd.FileName;
|
||||
string relativePath = UpdaterUtil.GetRelativePath(Path.GetFullPath(file), Environment.CurrentDirectory);
|
||||
string destinationPath = relativePath;
|
||||
|
||||
//copy file to XML path if it's not located relative to the game's files
|
||||
if (relativePath.StartsWith("..") ||
|
||||
Path.GetPathRoot(Environment.CurrentDirectory) != Path.GetPathRoot(file))
|
||||
{
|
||||
destinationPath = Path.Combine(Path.GetDirectoryName(XMLPath), Path.GetFileName(file));
|
||||
|
||||
string destinationDir = Path.GetDirectoryName(destinationPath);
|
||||
if (!Directory.Exists(destinationDir))
|
||||
{
|
||||
Directory.CreateDirectory(destinationDir);
|
||||
}
|
||||
|
||||
if (!File.Exists(destinationPath))
|
||||
{
|
||||
File.Copy(file, Path.GetFullPath(destinationPath), overwrite: true);
|
||||
}
|
||||
}
|
||||
|
||||
isTextureSelected = true;
|
||||
texturePathElement.Text = ToolBox.ConvertAbsoluteToRelativePath(ofd.FileName);
|
||||
texturePathElement.Text = destinationPath;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -84,7 +85,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
#else
|
||||
FetchRemoteContent(Frame.RectTransform);
|
||||
FetchRemoteContent();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -753,12 +754,20 @@ namespace Barotrauma
|
||||
exeName = "DedicatedServer.exe";
|
||||
}
|
||||
|
||||
string arguments = "-name \"" + name.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" +
|
||||
string arguments = "-name \"" + ToolBox.EscapeCharacters(name) + "\"" +
|
||||
" -public " + isPublicBox.Selected.ToString() +
|
||||
" -playstyle " + ((PlayStyle)playstyleBanner.UserData).ToString() +
|
||||
" -password \"" + passwordBox.Text.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" +
|
||||
" -maxplayers " + maxPlayersBox.Text;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(passwordBox.Text))
|
||||
{
|
||||
arguments += " -password \"" + ToolBox.EscapeCharacters(passwordBox.Text) + "\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments += " -nopassword";
|
||||
}
|
||||
|
||||
int ownerKey = 0;
|
||||
|
||||
if (Steam.SteamManager.GetSteamID()!=0)
|
||||
@@ -774,6 +783,7 @@ namespace Barotrauma
|
||||
string filename = exeName;
|
||||
#if LINUX || OSX
|
||||
filename = "./" + Path.GetFileNameWithoutExtension(exeName);
|
||||
arguments = ToolBox.EscapeCharacters(arguments);
|
||||
#endif
|
||||
var processInfo = new ProcessStartInfo
|
||||
{
|
||||
@@ -1149,34 +1159,15 @@ namespace Barotrauma
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void FetchRemoteContent(RectTransform parent)
|
||||
private void FetchRemoteContent()
|
||||
{
|
||||
if (string.IsNullOrEmpty(GameMain.Config.RemoteContentUrl)) { return; }
|
||||
try
|
||||
{
|
||||
var client = new RestClient(GameMain.Config.RemoteContentUrl);
|
||||
var request = new RestRequest("MenuContent.xml", Method.GET);
|
||||
|
||||
IRestResponse response = client.Execute(request);
|
||||
if (response.ResponseStatus != ResponseStatus.Completed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string xml = response.Content;
|
||||
int index = xml.IndexOf('<');
|
||||
if (index > 0) { xml = xml.Substring(index, xml.Length - index); }
|
||||
if (string.IsNullOrWhiteSpace(xml)) { return; }
|
||||
|
||||
XElement element = XDocument.Parse(xml)?.Root;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
GUIComponent.FromXML(subElement, parent);
|
||||
}
|
||||
client.ExecuteAsync(request, RemoteContentReceived);
|
||||
CoroutineManager.StartCoroutine(WairForRemoteContentReceived());
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
@@ -1189,5 +1180,60 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<object> WairForRemoteContentReceived()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
lock (remoteContentLock)
|
||||
{
|
||||
if (remoteContentResponse != null) { break; }
|
||||
}
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
}
|
||||
lock (remoteContentLock)
|
||||
{
|
||||
if (remoteContentResponse.ResponseStatus != ResponseStatus.Completed || remoteContentResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string xml = remoteContentResponse.Content;
|
||||
int index = xml.IndexOf('<');
|
||||
if (index > 0) { xml = xml.Substring(index, xml.Length - index); }
|
||||
if (!string.IsNullOrWhiteSpace(xml))
|
||||
{
|
||||
XElement element = XDocument.Parse(xml)?.Root;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
GUIComponent.FromXML(subElement, Frame.RectTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Reading received remote main menu content failed.", e);
|
||||
#endif
|
||||
GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.WairForRemoteContentReceived:Exception", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"Reading received remote main menu content failed. " + e.Message);
|
||||
}
|
||||
}
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
private readonly object remoteContentLock = new object();
|
||||
private IRestResponse remoteContentResponse;
|
||||
|
||||
private void RemoteContentReceived(IRestResponse response, RestRequestAsyncHandle handle)
|
||||
{
|
||||
lock (remoteContentLock)
|
||||
{
|
||||
remoteContentResponse = response;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,6 +319,14 @@ namespace Barotrauma
|
||||
RelativeSpacing = panelSpacing
|
||||
};
|
||||
|
||||
GameMain.Instance.OnResolutionChanged += () =>
|
||||
{
|
||||
if (innerFrame != null)
|
||||
{
|
||||
innerFrame.RectTransform.MaxSize = new Point(int.MaxValue, GameMain.GraphicsHeight - 50);
|
||||
}
|
||||
};
|
||||
|
||||
var panelContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), innerFrame.RectTransform, Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
@@ -440,6 +448,14 @@ namespace Barotrauma
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GameMain.Instance.OnResolutionChanged += () =>
|
||||
{
|
||||
if (panelContainer != null && sideBar != null)
|
||||
{
|
||||
sideBar.RectTransform.MaxSize = new Point(650, panelContainer.RectTransform.Rect.Height);
|
||||
}
|
||||
};
|
||||
|
||||
//player info panel ------------------------------------------------------------
|
||||
|
||||
myCharacterFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), sideBar.RectTransform));
|
||||
@@ -1781,24 +1797,23 @@ namespace Barotrauma
|
||||
RelativeSpacing = 0.03f
|
||||
};
|
||||
|
||||
var headerContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true)
|
||||
var headerContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var nameText = new GUITextBlock(new RectTransform(new Vector2(0.75f, 1.0f), headerContainer.RectTransform),
|
||||
var nameText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), headerContainer.RectTransform),
|
||||
text: selectedClient.Name, font: GUI.LargeFont);
|
||||
nameText.Text = ToolBox.LimitString(nameText.Text, nameText.Font, nameText.Rect.Width);
|
||||
|
||||
if (selectedClient.SteamID != 0 && Steam.SteamManager.IsInitialized)
|
||||
{
|
||||
var viewSteamProfileButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), headerContainer.RectTransform, Anchor.TopCenter),
|
||||
var viewSteamProfileButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), headerContainer.RectTransform, Anchor.TopCenter) { MaxSize = new Point(int.MaxValue, (int)(40 * GUI.Scale)) },
|
||||
TextManager.Get("ViewSteamProfile"))
|
||||
{
|
||||
UserData = selectedClient
|
||||
};
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(nameText, viewSteamProfileButton.TextBlock);
|
||||
|
||||
viewSteamProfileButton.TextBlock.AutoScale = true;
|
||||
viewSteamProfileButton.OnClicked = (bt, userdata) =>
|
||||
{
|
||||
Steam.SteamManager.Instance.Overlay.OpenUrl("https://steamcommunity.com/profiles/" + selectedClient.SteamID.ToString());
|
||||
@@ -2054,7 +2069,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var closeButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaLower.RectTransform, Anchor.BottomRight),
|
||||
TextManager.Get("Close"))
|
||||
TextManager.Get("Close"), style: "GUIButtonLarge")
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
OnClicked = ClosePlayerFrame
|
||||
@@ -2409,6 +2424,13 @@ namespace Barotrauma
|
||||
AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - characterInfoFrame.Rect.Width, button.Rect.Bottom)
|
||||
});
|
||||
|
||||
characterInfoFrame.RectTransform.SizeChanged += () =>
|
||||
{
|
||||
if (characterInfoFrame == null || HeadSelectionList?.RectTransform == null || button == null) { return; }
|
||||
HeadSelectionList.RectTransform.Resize(new Point(characterInfoFrame.Rect.Width, (characterInfoFrame.Rect.Bottom - button.Rect.Bottom) + characterInfoFrame.Rect.Height * 2));
|
||||
HeadSelectionList.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - characterInfoFrame.Rect.Width, button.Rect.Bottom);
|
||||
};
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), HeadSelectionList.RectTransform, Anchor.Center), style: "OuterGlow", color: Color.Black)
|
||||
{
|
||||
UserData = "outerglow",
|
||||
@@ -2446,7 +2468,7 @@ namespace Barotrauma
|
||||
headSprite.SourceRect = new Rectangle(CharacterInfo.CalculateOffset(headSprite, head.Value.ToPoint()), headSprite.SourceRect.Size);
|
||||
characterSprites.Add(headSprite);
|
||||
|
||||
if (row == null || itemsInRow >= 4)
|
||||
if (itemsInRow >= 4 || row == null || gender != (Gender)row.UserData)
|
||||
{
|
||||
row = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.333f), HeadSelectionList.Content.RectTransform), true)
|
||||
{
|
||||
@@ -2536,6 +2558,14 @@ namespace Barotrauma
|
||||
JobSelectionFrame = new GUIFrame(new RectTransform(frameSize, GUI.Canvas, Anchor.TopLeft)
|
||||
{ AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - frameSize.X, characterInfoFrame.Rect.Bottom) }, "GUIFrameListBox");
|
||||
|
||||
characterInfoFrame.RectTransform.SizeChanged += () =>
|
||||
{
|
||||
if (characterInfoFrame == null || JobSelectionFrame?.RectTransform == null) { return; }
|
||||
Point size = new Point(characterInfoFrame.Rect.Width, characterInfoFrame.Rect.Height * 2);
|
||||
JobSelectionFrame.RectTransform.Resize(size);
|
||||
JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - size.X, characterInfoFrame.Rect.Bottom);
|
||||
};
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), JobSelectionFrame.RectTransform, Anchor.Center), style: "OuterGlow", color: Color.Black)
|
||||
{
|
||||
UserData = "outerglow",
|
||||
@@ -2600,32 +2630,24 @@ namespace Barotrauma
|
||||
image.Visible = currVisible == (variantIndex + 1);
|
||||
}
|
||||
|
||||
var variantButton = new GUIButton(new RectTransform(new Vector2(0.15f), jobButton.RectTransform, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.05f, 0.05f + 0.2f * variantIndex) }, (variantIndex + 1).ToString(), style: null)
|
||||
var variantButton = CreateJobVariantButton(jobPrefab, variantIndex, images.Length, jobButton);
|
||||
variantButton.OnClicked = (btn, obj) =>
|
||||
{
|
||||
Color = new Color(50, 50, 50, 200),
|
||||
HoverColor = Color.Gray * 0.75f,
|
||||
PressedColor = Color.Black * 0.75f,
|
||||
SelectedColor = new Color(45, 70, 100, 200),
|
||||
UserData = new Pair<JobPrefab, int>(jobPrefab.First, variantIndex+1),
|
||||
OnClicked = (btn, obj) =>
|
||||
currSelected.Selected = false;
|
||||
int k = ((Pair<JobPrefab, int>)obj).Second;
|
||||
btn.Parent.UserData = obj;
|
||||
for (int j = 0; j < images.Length; j++)
|
||||
{
|
||||
currSelected.Selected = false;
|
||||
int k = ((Pair<JobPrefab, int>)obj).Second;
|
||||
btn.Parent.UserData = obj;
|
||||
for (int j = 0; j < images.Length; j++)
|
||||
foreach (GUIImage image in images[j])
|
||||
{
|
||||
foreach (GUIImage image in images[j])
|
||||
{
|
||||
image.Visible = k == (j + 1);
|
||||
}
|
||||
image.Visible = k == (j + 1);
|
||||
}
|
||||
currSelected = btn;
|
||||
currSelected.Selected = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
currSelected = btn;
|
||||
currSelected.Selected = true;
|
||||
|
||||
return false;
|
||||
};
|
||||
if (currVisible == (variantIndex + 1))
|
||||
{
|
||||
currSelected = variantButton;
|
||||
@@ -2818,7 +2840,7 @@ namespace Barotrauma
|
||||
|
||||
subList.Enabled = !enabled && AllowSubSelection;
|
||||
shuttleList.Enabled = !enabled && GameMain.Client.HasPermission(ClientPermissions.SelectSub);
|
||||
StartButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ManageRound) && GameMain.Client.GameStarted && !enabled;
|
||||
StartButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ManageRound) && !GameMain.Client.GameStarted && !enabled;
|
||||
|
||||
if (campaignViewButton != null) { campaignViewButton.Visible = enabled; }
|
||||
|
||||
@@ -2943,34 +2965,28 @@ namespace Barotrauma
|
||||
}
|
||||
if (images.Length > 1)
|
||||
{
|
||||
var variantButton = new GUIButton(new RectTransform(new Vector2(0.15f), slot.RectTransform, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.05f, 0.25f + 0.2f * variantIndex) }, (variantIndex + 1).ToString(), style: null)
|
||||
var variantButton = CreateJobVariantButton(jobPrefab, variantIndex, images.Length, slot);
|
||||
variantButton.OnClicked = (btn, obj) =>
|
||||
{
|
||||
Color = new Color(50, 50, 50, 200),
|
||||
HoverColor = Color.Gray * 0.75f,
|
||||
PressedColor = Color.Black * 0.75f,
|
||||
SelectedColor = new Color(45, 70, 100, 200),
|
||||
Selected = jobPrefab.Second == (variantIndex + 1),
|
||||
UserData = new Pair<JobPrefab, int>(jobPrefab.First, variantIndex + 1),
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
int k = ((Pair<JobPrefab, int>)obj).Second;
|
||||
btn.Parent.UserData = obj;
|
||||
UpdateJobPreferences(listBox);
|
||||
return false;
|
||||
}
|
||||
int k = ((Pair<JobPrefab, int>)obj).Second;
|
||||
btn.Parent.UserData = obj;
|
||||
UpdateJobPreferences(listBox);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//info button
|
||||
new GUIButton(new RectTransform(new Vector2(0.15f), slot.RectTransform, Anchor.TopLeft, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.05f) }, style: "GUIButtonInfo")
|
||||
new GUIButton(new RectTransform(new Vector2(0.2f), slot.RectTransform, Anchor.TopLeft, scaleBasis: ScaleBasis.BothHeight)
|
||||
{ RelativeOffset = new Vector2(0.05f) },
|
||||
style: "GUIButtonInfo")
|
||||
{
|
||||
UserData = jobPrefab.First,
|
||||
OnClicked = ViewJobInfo
|
||||
};
|
||||
|
||||
//remove button
|
||||
new GUIButton(new RectTransform(new Vector2(0.15f), slot.RectTransform, Anchor.TopRight, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.05f) }, style: "GUICancelButton")
|
||||
new GUIButton(new RectTransform(new Vector2(0.2f), slot.RectTransform, Anchor.TopRight, scaleBasis: ScaleBasis.BothHeight) { RelativeOffset = new Vector2(0.05f) }, style: "GUICancelButton")
|
||||
{
|
||||
UserData = i,
|
||||
OnClicked = (btn, obj) =>
|
||||
@@ -3013,6 +3029,24 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private GUIButton CreateJobVariantButton(Pair<JobPrefab, int> jobPrefab, int variantIndex, int variantCount, GUIComponent slot)
|
||||
{
|
||||
float relativeHeight = Math.Min(0.7f / variantCount, 0.2f);
|
||||
|
||||
var btn = new GUIButton(new RectTransform(new Vector2(relativeHeight), slot.RectTransform, scaleBasis: ScaleBasis.BothHeight)
|
||||
{ RelativeOffset = new Vector2(0.05f, 0.25f + relativeHeight * 1.05f * variantIndex) },
|
||||
(variantIndex + 1).ToString(), style: null)
|
||||
{
|
||||
Color = new Color(50, 50, 50, 200),
|
||||
HoverColor = Color.Gray * 0.75f,
|
||||
PressedColor = Color.Black * 0.75f,
|
||||
SelectedColor = new Color(45, 70, 100, 200),
|
||||
Selected = jobPrefab.Second == (variantIndex + 1),
|
||||
UserData = new Pair<JobPrefab, int>(jobPrefab.First, variantIndex + 1),
|
||||
};
|
||||
return btn;
|
||||
}
|
||||
|
||||
public Pair<string, string> FailedSelectedSub;
|
||||
public Pair<string, string> FailedSelectedShuttle;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -426,7 +427,7 @@ namespace Barotrauma
|
||||
ToolTip = mode.Name,
|
||||
Selected = true,
|
||||
OnSelected = (tickBox) => { FilterServers(); return true; },
|
||||
UserData = mode.Name
|
||||
UserData = mode.Identifier
|
||||
};
|
||||
gameModeTickBoxes.Add(selectionTick);
|
||||
filterTextList.Add(selectionTick.TextBlock);
|
||||
@@ -736,6 +737,7 @@ namespace Barotrauma
|
||||
info.PlayerCount = GameMain.Client.ConnectedClients.Count;
|
||||
info.PingChecked = false;
|
||||
info.HasPassword = serverSettings.HasPassword;
|
||||
info.OwnerVerified = true;
|
||||
|
||||
if (isInfoNew)
|
||||
{
|
||||
@@ -747,6 +749,12 @@ namespace Barotrauma
|
||||
|
||||
public void AddToRecentServers(ServerInfo info)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(info.IP))
|
||||
{
|
||||
//don't add localhost to recent servers
|
||||
if (IPAddress.TryParse(info.IP, out IPAddress ip) && IPAddress.IsLoopback(ip)) { return; }
|
||||
}
|
||||
|
||||
info.Recent = true;
|
||||
ServerInfo existingInfo = recentServers.Find(serverInfo => info.OwnerID == serverInfo.OwnerID && (info.OwnerID != 0 ? true : (info.IP == serverInfo.IP && info.Port == serverInfo.Port)));
|
||||
if (existingInfo == null)
|
||||
@@ -898,6 +906,11 @@ namespace Barotrauma
|
||||
base.Select();
|
||||
SelectedTab = ServerListTab.All;
|
||||
RefreshServers();
|
||||
|
||||
if (SteamManager.IsInitialized && SteamManager.Instance.LobbyList != null)
|
||||
{
|
||||
SteamManager.Instance.LobbyList.OnLobbyDataReceived = OnLobbyDataReceived;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deselect()
|
||||
@@ -905,7 +918,7 @@ namespace Barotrauma
|
||||
base.Deselect();
|
||||
if (SteamManager.IsInitialized && SteamManager.Instance.LobbyList != null)
|
||||
{
|
||||
SteamManager.Instance.LobbyList.OnLobbiesUpdated = null;
|
||||
SteamManager.Instance.LobbyList.OnLobbyDataReceived = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -947,6 +960,7 @@ namespace Barotrauma
|
||||
(remoteVersion != null && !NetworkMember.IsCompatible(GameMain.Version, remoteVersion));
|
||||
|
||||
child.Visible =
|
||||
serverInfo.OwnerVerified &&
|
||||
serverInfo.ServerName.ToLowerInvariant().Contains(searchBox.Text.ToLowerInvariant()) &&
|
||||
(!filterSameVersion.Selected || (remoteVersion != null && NetworkMember.IsCompatible(remoteVersion, GameMain.Version))) &&
|
||||
(!filterPassword.Selected || !serverInfo.HasPassword) &&
|
||||
@@ -1405,7 +1419,8 @@ namespace Barotrauma
|
||||
{
|
||||
yield return new WaitForSeconds((float)(refreshDisableTimer - DateTime.Now).TotalSeconds);
|
||||
}
|
||||
|
||||
|
||||
recentServers.Concat(favoriteServers).ForEach(si => si.OwnerVerified = false);
|
||||
if (GameMain.Config.UseSteamMatchmaking)
|
||||
{
|
||||
serverList.ClearChildren();
|
||||
@@ -1479,7 +1494,8 @@ namespace Barotrauma
|
||||
PlayerCount = playerCount,
|
||||
MaxPlayers = maxPlayers,
|
||||
HasPassword = hasPassWord,
|
||||
GameVersion = gameVersion
|
||||
GameVersion = gameVersion,
|
||||
OwnerVerified = true
|
||||
};
|
||||
foreach (string contentPackageName in contentPackageNames.Split(','))
|
||||
{
|
||||
@@ -1511,6 +1527,37 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLobbyDataReceived(Facepunch.Steamworks.LobbyList.Lobby lobby)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(lobby.GetData("haspassword"))) { return; }
|
||||
bool.TryParse(lobby.GetData("haspassword"), out bool hasPassword);
|
||||
int.TryParse(lobby.GetData("playercount"), out int currPlayers);
|
||||
int.TryParse(lobby.GetData("maxplayernum"), out int maxPlayers);
|
||||
//UInt64.TryParse(lobby.GetData("connectsteamid"), out ulong connectSteamId);
|
||||
string ip = lobby.GetData("hostipaddress");
|
||||
UInt64 ownerId = SteamManager.SteamIDStringToUInt64(lobby.GetData("lobbyowner"));
|
||||
|
||||
ServerInfo newInfo = new ServerInfo();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ip)) { ip = ""; }
|
||||
|
||||
newInfo.ServerName = lobby.Name;
|
||||
newInfo.Port = "";
|
||||
newInfo.QueryPort = "";
|
||||
newInfo.IP = ip;
|
||||
newInfo.PlayerCount = currPlayers;
|
||||
newInfo.MaxPlayers = maxPlayers;
|
||||
newInfo.HasPassword = hasPassword;
|
||||
newInfo.RespondedToSteamQuery = true;
|
||||
newInfo.LobbyID = lobby.LobbyID;
|
||||
newInfo.OwnerID = ownerId;
|
||||
newInfo.PingChecked = false;
|
||||
newInfo.OwnerVerified = true;
|
||||
SteamManager.AssignLobbyDataToServerInfo(lobby, newInfo);
|
||||
|
||||
AddToServerList(newInfo);
|
||||
}
|
||||
|
||||
private void AddToServerList(ServerInfo serverInfo)
|
||||
{
|
||||
var serverFrame = serverList.Content.FindChild(d => (d.UserData is ServerInfo info) &&
|
||||
@@ -1552,7 +1599,8 @@ namespace Barotrauma
|
||||
if (serverInfo.OwnerVerified)
|
||||
{
|
||||
DebugConsole.NewMessage(serverInfo.OwnerID + " verified!");
|
||||
var childrenToRemove = serverList.Content.FindChildren(c => (c.UserData is ServerInfo info) && info != serverInfo && info.OwnerID == serverInfo.OwnerID).ToList();
|
||||
var childrenToRemove = serverList.Content.FindChildren(c => (c.UserData is ServerInfo info) && info != serverInfo &&
|
||||
(serverInfo.OwnerID != 0 ? info.OwnerID == serverInfo.OwnerID : info.IP == serverInfo.IP)).ToList();
|
||||
foreach (var child in childrenToRemove)
|
||||
{
|
||||
serverList.Content.RemoveChild(child);
|
||||
@@ -1594,7 +1642,13 @@ namespace Barotrauma
|
||||
UserData = "password"
|
||||
};
|
||||
|
||||
var serverName = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[2] * 1.1f, 1.0f), serverContent.RectTransform), serverInfo.ServerName, style: "GUIServerListTextBox");
|
||||
var serverName = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[2] * 1.1f, 1.0f), serverContent.RectTransform),
|
||||
#if !DEBUG
|
||||
serverInfo.ServerName,
|
||||
#else
|
||||
((serverInfo.OwnerID != 0 || serverInfo.LobbyID != 0) ? "[STEAMP2P] " : "[LIDGREN] ") + serverInfo.ServerName,
|
||||
#endif
|
||||
style: "GUIServerListTextBox");
|
||||
|
||||
new GUITickBox(new RectTransform(new Vector2(columnRelativeWidth[3], 0.9f), serverContent.RectTransform, Anchor.Center), label: "")
|
||||
{
|
||||
@@ -1793,7 +1847,7 @@ namespace Barotrauma
|
||||
GameMain.Config.PlayerName = clientNameBox.Text;
|
||||
GameMain.Config.SaveNewPlayerConfig();
|
||||
|
||||
CoroutineManager.StartCoroutine(ConnectToServer(endpoint, serverName));
|
||||
CoroutineManager.StartCoroutine(ConnectToServer(endpoint, serverName), "ConnectToServer");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1822,40 +1876,31 @@ namespace Barotrauma
|
||||
|
||||
public void GetServerPing(ServerInfo serverInfo, GUITextBlock serverPingText)
|
||||
{
|
||||
if (activePings.Contains(serverInfo.IP)) { return; }
|
||||
activePings.Add(serverInfo.IP);
|
||||
if (CoroutineManager.IsCoroutineRunning("ConnectToServer")) { return; }
|
||||
|
||||
lock (activePings)
|
||||
{
|
||||
if (activePings.Contains(serverInfo.IP)) { return; }
|
||||
activePings.Add(serverInfo.IP);
|
||||
}
|
||||
|
||||
serverInfo.PingChecked = false;
|
||||
serverInfo.Ping = -1;
|
||||
|
||||
var pingThread = new Thread(() => { PingServer(serverInfo, 1000); })
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
pingThread.Start();
|
||||
|
||||
CoroutineManager.StartCoroutine(UpdateServerPingText(serverInfo, serverPingText, 1000));
|
||||
}
|
||||
|
||||
private IEnumerable<object> UpdateServerPingText(ServerInfo serverInfo, GUITextBlock serverPingText, int timeOut)
|
||||
{
|
||||
DateTime timeOutTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: timeOut);
|
||||
while (DateTime.Now < timeOutTime)
|
||||
{
|
||||
if (serverInfo.PingChecked)
|
||||
TaskPool.Add(PingServerAsync(serverInfo?.IP, 1000),
|
||||
new Tuple<ServerInfo, GUITextBlock>(serverInfo, serverPingText),
|
||||
(rtt, obj) =>
|
||||
{
|
||||
if (serverInfo.Ping != -1)
|
||||
var info = obj.Item1;
|
||||
var text = obj.Item2;
|
||||
info.Ping = rtt.Result; info.PingChecked = true;
|
||||
text.TextColor = GetPingTextColor(info.Ping);
|
||||
text.Text = info.Ping > -1 ? info.Ping.ToString() : "?";
|
||||
lock (activePings)
|
||||
{
|
||||
serverPingText.TextColor = GetPingTextColor(serverInfo.Ping);
|
||||
}
|
||||
serverPingText.Text = serverInfo.Ping > -1 ? serverInfo.Ping.ToString() : "?";
|
||||
activePings.Remove(serverInfo.IP);
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
yield return CoroutineStatus.Success;
|
||||
activePings.Remove(serverInfo.IP);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Color GetPingTextColor(int ping)
|
||||
@@ -1864,18 +1909,27 @@ namespace Barotrauma
|
||||
return ToolBox.GradientLerp(ping / 200.0f, Color.LightGreen, Color.Yellow * 0.8f, Color.Red * 0.75f);
|
||||
}
|
||||
|
||||
public void PingServer(ServerInfo serverInfo, int timeOut)
|
||||
public async Task<int> PingServerAsync(string ip, int timeOut)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(serverInfo?.IP))
|
||||
await Task.Yield();
|
||||
int activePingCount = 100;
|
||||
while (activePingCount > 25)
|
||||
{
|
||||
serverInfo.PingChecked = true;
|
||||
serverInfo.Ping = -1;
|
||||
return;
|
||||
lock (activePings)
|
||||
{
|
||||
activePingCount = activePings.Count;
|
||||
}
|
||||
await Task.Delay(25);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ip))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
long rtt = -1;
|
||||
IPAddress address = null;
|
||||
IPAddress.TryParse(serverInfo.IP, out address);
|
||||
IPAddress.TryParse(ip, out address);
|
||||
if (address != null)
|
||||
{
|
||||
//don't attempt to ping if the address is IPv6 and it's not supported
|
||||
@@ -1902,8 +1956,8 @@ namespace Barotrauma
|
||||
}
|
||||
catch (PingException ex)
|
||||
{
|
||||
string errorMsg = "Failed to ping a server (" + serverInfo.ServerName + ", " + serverInfo.IP + ") - " + (ex?.InnerException?.Message ?? ex.Message);
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + serverInfo.IP, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, errorMsg);
|
||||
string errorMsg = "Failed to ping a server (" + ip + ") - " + (ex?.InnerException?.Message ?? ex.Message);
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + ip, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage(errorMsg, Color.Red);
|
||||
#endif
|
||||
@@ -1911,10 +1965,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
serverInfo.PingChecked = true;
|
||||
serverInfo.Ping = (int)rtt;
|
||||
return (int)rtt;
|
||||
}
|
||||
|
||||
|
||||
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
|
||||
{
|
||||
graphics.Clear(Color.CornflowerBlue);
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
||||
|
||||
private List<GUIButton> tabButtons = new List<GUIButton>();
|
||||
|
||||
private HashSet<string> pendingPreviewImageDownloads = new HashSet<string>();
|
||||
private readonly HashSet<string> pendingPreviewImageDownloads = new HashSet<string>();
|
||||
private Dictionary<string, Sprite> itemPreviewSprites = new Dictionary<string, Sprite>();
|
||||
|
||||
private enum Tab
|
||||
@@ -379,10 +379,16 @@ namespace Barotrauma
|
||||
if (!string.IsNullOrEmpty(item.PreviewImageUrl))
|
||||
{
|
||||
string imagePreviewPath = Path.Combine(SteamManager.WorkshopItemPreviewImageFolder, item.Id + ".png");
|
||||
if (!pendingPreviewImageDownloads.Contains(item.PreviewImageUrl))
|
||||
{
|
||||
pendingPreviewImageDownloads.Add(item.PreviewImageUrl);
|
||||
|
||||
bool isNewImage;
|
||||
lock (pendingPreviewImageDownloads)
|
||||
{
|
||||
isNewImage = !pendingPreviewImageDownloads.Contains(item.PreviewImageUrl);
|
||||
if (isNewImage) { pendingPreviewImageDownloads.Add(item.PreviewImageUrl); }
|
||||
}
|
||||
|
||||
if (isNewImage)
|
||||
{
|
||||
if (File.Exists(imagePreviewPath))
|
||||
{
|
||||
File.Delete(imagePreviewPath);
|
||||
@@ -397,10 +403,13 @@ namespace Barotrauma
|
||||
var request = new RestRequest(fileName, Method.GET);
|
||||
client.ExecuteAsync(request, response =>
|
||||
{
|
||||
pendingPreviewImageDownloads.Remove(item.PreviewImageUrl);
|
||||
lock (pendingPreviewImageDownloads)
|
||||
{
|
||||
pendingPreviewImageDownloads.Remove(item.PreviewImageUrl);
|
||||
}
|
||||
OnPreviewImageDownloaded(response, imagePreviewPath);
|
||||
CoroutineManager.StartCoroutine(WaitForItemPreviewDownloaded(item, listBox, imagePreviewPath));
|
||||
});
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -410,7 +419,10 @@ namespace Barotrauma
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
pendingPreviewImageDownloads.Remove(item.PreviewImageUrl);
|
||||
lock (pendingPreviewImageDownloads)
|
||||
{
|
||||
pendingPreviewImageDownloads.Remove(item.PreviewImageUrl);
|
||||
}
|
||||
DebugConsole.ThrowError("Downloading the preview image of the Workshop item \"" + TextManager.EnsureUTF8(item.Title) + "\" failed.", e);
|
||||
}
|
||||
}
|
||||
@@ -595,8 +607,13 @@ namespace Barotrauma
|
||||
|
||||
private IEnumerable<object> WaitForItemPreviewDownloaded(Facepunch.Steamworks.Workshop.Item item, GUIListBox listBox, string previewImagePath)
|
||||
{
|
||||
while (pendingPreviewImageDownloads.Contains(item.PreviewImageUrl))
|
||||
while (true)
|
||||
{
|
||||
lock (pendingPreviewImageDownloads)
|
||||
{
|
||||
if (!pendingPreviewImageDownloads.Contains(item.PreviewImageUrl)) { break; }
|
||||
}
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
|
||||
@@ -522,11 +522,6 @@ namespace Barotrauma
|
||||
OnSelected = (GUITickBox obj) => { Gap.ShowGaps = obj.Selected; return true; },
|
||||
};
|
||||
|
||||
tickBoxHolder.Children.ForEach(c =>
|
||||
{
|
||||
if (c is GUITickBox tb) { tb.RectTransform.MinSize = new Point(0, 32); }
|
||||
});
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(tickBoxHolder.Children.Where(c => c is GUITickBox).Select(c => ((GUITickBox)c).TextBlock));
|
||||
|
||||
//spacing
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Barotrauma.SpriteDeformations
|
||||
/// A positive value means that this deformation is or could be used for multiple sprites.
|
||||
/// This behaviour is not automatic, and has to be implemented for any particular case separately (currently only used in Limbs).
|
||||
/// </summary>
|
||||
[Serialize(-1, true), Editable]
|
||||
[Serialize(-1, true), Editable(minValue: -1, maxValue: 100)]
|
||||
public int Sync
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (RoundSound sound in sounds)
|
||||
{
|
||||
if (sound.Sound == null)
|
||||
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);
|
||||
@@ -90,7 +90,7 @@ namespace Barotrauma
|
||||
selectedSoundIndex = Rand.Int(sounds.Count);
|
||||
}
|
||||
var selectedSound = sounds[selectedSoundIndex];
|
||||
if (selectedSound.Sound == null)
|
||||
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);
|
||||
|
||||
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.9.5.1")]
|
||||
[assembly: AssemblyFileVersion("0.9.5.1")]
|
||||
[assembly: AssemblyVersion("0.9.6.0")]
|
||||
[assembly: AssemblyFileVersion("0.9.6.0")]
|
||||
|
||||
@@ -268,8 +268,8 @@ namespace Barotrauma
|
||||
break;
|
||||
case NetEntityEvent.Type.Control:
|
||||
msg.WriteRangedInteger(1, 0, 3);
|
||||
Client owner = ((Client)extraData[1]);
|
||||
msg.Write(owner == null ? (byte)0 : owner.ID);
|
||||
Client owner = (Client)extraData[1];
|
||||
msg.Write(owner != null && owner.Character == this && GameMain.Server.ConnectedClients.Contains(owner) ? owner.ID : (byte)0);
|
||||
break;
|
||||
case NetEntityEvent.Type.Status:
|
||||
msg.WriteRangedInteger(2, 0, 3);
|
||||
@@ -445,14 +445,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteSpawnData(IWriteMessage msg)
|
||||
public void WriteSpawnData(IWriteMessage msg, UInt16 entityId)
|
||||
{
|
||||
if (GameMain.Server == null) return;
|
||||
|
||||
int msgLength = msg.LengthBytes;
|
||||
|
||||
msg.Write(Info == null);
|
||||
msg.Write(ID);
|
||||
msg.Write(entityId);
|
||||
msg.Write(SpeciesName);
|
||||
msg.Write(seed);
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace Barotrauma
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
CommandLineArgs = args;
|
||||
CommandLineArgs = ToolBox.MergeArguments(args);
|
||||
|
||||
World = new World(new Vector2(0, -9.82f));
|
||||
FarseerPhysics.Settings.AllowSleep = true;
|
||||
@@ -189,6 +189,13 @@ namespace Barotrauma
|
||||
ownerKey = 0;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
foreach (string s in CommandLineArgs)
|
||||
{
|
||||
Console.WriteLine(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < CommandLineArgs.Length; i++)
|
||||
{
|
||||
switch (CommandLineArgs[i].Trim())
|
||||
@@ -213,6 +220,9 @@ namespace Barotrauma
|
||||
password = CommandLineArgs[i + 1];
|
||||
i++;
|
||||
break;
|
||||
case "-nopassword":
|
||||
password = "";
|
||||
break;
|
||||
case "-upnp":
|
||||
case "-enableupnp":
|
||||
bool.TryParse(CommandLineArgs[i + 1], out enableUpnp);
|
||||
@@ -303,6 +313,7 @@ namespace Barotrauma
|
||||
Server.Update((float)Timing.Step);
|
||||
if (Server == null) { break; }
|
||||
SteamManager.Update((float)Timing.Step);
|
||||
TaskPool.Update();
|
||||
CoroutineManager.Update((float)Timing.Step, (float)Timing.Step);
|
||||
|
||||
Timing.Accumulator -= Timing.Step;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage, bool forcedOpen)
|
||||
{
|
||||
if (isStuck || isOpen == open)
|
||||
if (IsStuck || isOpen == open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Barotrauma.Items.Components
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(state);
|
||||
msg.Write(user == null ? (ushort)0 : user.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Barotrauma.Items.Components
|
||||
msg.Write(deteriorationTimer);
|
||||
msg.Write(deteriorateAlwaysResetTimer);
|
||||
msg.Write(DeteriorateAlways);
|
||||
msg.Write(CurrentFixer == c.Character);
|
||||
msg.Write(CurrentFixer == null ? (ushort)0 : CurrentFixer.ID);
|
||||
msg.WriteRangedInteger((int)currentFixerAction, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<Wire> clientSideDisconnectedWires = new List<Wire>();
|
||||
ushort disconnectedWireCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < disconnectedWireCount; i++)
|
||||
@@ -179,6 +178,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(user == null ? (ushort)0 : user.ID);
|
||||
ClientWrite(msg, extraData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,16 +100,26 @@ namespace Barotrauma
|
||||
{
|
||||
ActionType actionType = (ActionType)extraData[1];
|
||||
ItemComponent targetComponent = extraData.Length > 2 ? (ItemComponent)extraData[2] : null;
|
||||
ushort targetID = extraData.Length > 3 ? (ushort)extraData[3] : (ushort)0;
|
||||
ushort characterID = extraData.Length > 3 ? (ushort)extraData[3] : (ushort)0;
|
||||
Limb targetLimb = extraData.Length > 4 ? (Limb)extraData[4] : null;
|
||||
ushort useTargetID = extraData.Length > 5 ? (ushort)extraData[5] : (ushort)0;
|
||||
Vector2? worldPosition = null;
|
||||
if (extraData.Length > 6) { worldPosition = (Vector2)extraData[6]; }
|
||||
|
||||
Character targetCharacter = FindEntityByID(targetID) as Character;
|
||||
Character targetCharacter = FindEntityByID(characterID) as Character;
|
||||
byte targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255;
|
||||
|
||||
msg.WriteRangedInteger((int)actionType, 0, Enum.GetValues(typeof(ActionType)).Length - 1);
|
||||
msg.Write((byte)(targetComponent == null ? 255 : components.IndexOf(targetComponent)));
|
||||
msg.Write(targetID);
|
||||
msg.Write(characterID);
|
||||
msg.Write(targetLimbIndex);
|
||||
msg.Write(useTargetID);
|
||||
msg.Write(worldPosition.HasValue);
|
||||
if (worldPosition.HasValue)
|
||||
{
|
||||
msg.Write(worldPosition.Value.X);
|
||||
msg.Write(worldPosition.Value.Y);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NetEntityEvent.Type.ChangeProperty:
|
||||
@@ -197,7 +207,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteSpawnData(IWriteMessage msg)
|
||||
public void WriteSpawnData(IWriteMessage msg, UInt16 entityID)
|
||||
{
|
||||
if (GameMain.Server == null) return;
|
||||
|
||||
@@ -209,7 +219,7 @@ namespace Barotrauma
|
||||
msg.Write(Description);
|
||||
}
|
||||
|
||||
msg.Write(ID);
|
||||
msg.Write(entityID);
|
||||
|
||||
if (ParentInventory == null || ParentInventory.Owner == null)
|
||||
{
|
||||
|
||||
@@ -25,24 +25,23 @@ namespace Barotrauma
|
||||
SpawnOrRemove entities = (SpawnOrRemove)extraData[0];
|
||||
|
||||
message.Write(entities.Remove);
|
||||
|
||||
if (entities.Remove)
|
||||
{
|
||||
message.Write(entities.Entity.ID);
|
||||
message.Write(entities.OriginalID);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entities.Entity is Item)
|
||||
{
|
||||
message.Write((byte)SpawnableType.Item);
|
||||
DebugConsole.Log("Writing item spawn data " + entities.Entity.ToString() + " (ID: " + entities.Entity.ID + ")");
|
||||
((Item)entities.Entity).WriteSpawnData(message);
|
||||
DebugConsole.Log("Writing item spawn data " + entities.Entity.ToString() + " (original ID: " + entities.OriginalID + ", current ID: " + entities.Entity.ID + ")");
|
||||
((Item)entities.Entity).WriteSpawnData(message, entities.OriginalID);
|
||||
}
|
||||
else if (entities.Entity is Character)
|
||||
{
|
||||
message.Write((byte)SpawnableType.Character);
|
||||
DebugConsole.Log("Writing character spawn data: " + entities.Entity.ToString() + " (ID: " + entities.Entity.ID + ")");
|
||||
((Character)entities.Entity).WriteSpawnData(message);
|
||||
DebugConsole.Log("Writing character spawn data: " + entities.Entity.ToString() + " (original ID: " + entities.OriginalID + ", current ID: " + entities.Entity.ID + ")");
|
||||
((Character)entities.Entity).WriteSpawnData(message, entities.OriginalID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,10 +134,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
serverSettings = new ServerSettings(this, name, port, queryPort, maxPlayers, isPublic, attemptUPnP);
|
||||
KarmaManager.SelectPreset(serverSettings.KarmaPreset);
|
||||
if (!string.IsNullOrEmpty(password))
|
||||
{
|
||||
serverSettings.SetPassword(password);
|
||||
}
|
||||
serverSettings.SetPassword(password);
|
||||
|
||||
ownerKey = ownKey;
|
||||
|
||||
@@ -874,7 +871,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (Level.Loaded != null && levelEqualityCheckVal != Level.Loaded.EqualityCheckVal)
|
||||
{
|
||||
errorStr += " Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ").";
|
||||
errorStr += " Level equality check failed. The level generated at your end doesn't match the level generated by the server(seed: " + Level.Loaded.Seed +
|
||||
", sub: " + Submarine.MainSub.Name + " (" + Submarine.MainSub.MD5Hash.ShortHash + ")" +
|
||||
", mirrored: " + Level.Loaded.Mirrored + ").";
|
||||
}
|
||||
|
||||
Log(c.Name + " has reported an error: " + errorStr, ServerLog.MessageType.Error);
|
||||
|
||||
@@ -306,6 +306,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
ServerName = doc.Root.GetAttributeString("name", "");
|
||||
if (ServerName.Length > NetConfig.ServerNameMaxLength) { ServerName = ServerName.Substring(0, NetConfig.ServerNameMaxLength); }
|
||||
ServerMessageText = doc.Root.GetAttributeString("ServerMessage", "");
|
||||
|
||||
GameMain.NetLobbyScreen.SelectedModeIdentifier = GameModeIdentifier;
|
||||
|
||||
@@ -283,6 +283,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\MTRandom.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\Rand.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\SaveUtil.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\TaskPool.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\ToolBox.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\UpdaterUtil.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// How long does it take for the ai target to fade out if not kept alive.
|
||||
/// </summary>
|
||||
public float FadeOutTime { get; private set; }
|
||||
public float FadeOutTime { get; private set; } = 1;
|
||||
|
||||
public bool Static { get; private set; }
|
||||
|
||||
|
||||
@@ -611,6 +611,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
bool canAttack = true;
|
||||
bool pursue = false;
|
||||
if (IsCoolDownRunning)
|
||||
{
|
||||
switch (AttackingLimb.attack.AfterAttack)
|
||||
@@ -623,6 +624,7 @@ namespace Barotrauma
|
||||
if (AttackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue)
|
||||
{
|
||||
canAttack = false;
|
||||
pursue = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -661,6 +663,7 @@ namespace Barotrauma
|
||||
if (AttackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue)
|
||||
{
|
||||
canAttack = false;
|
||||
pursue = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -853,32 +856,35 @@ namespace Barotrauma
|
||||
if (pathSteering.CurrentPath != null)
|
||||
{
|
||||
// Attack doors
|
||||
if (canAttackSub && pathSteering.CurrentPath.CurrentNode?.ConnectedDoor != null && SelectedAiTarget != pathSteering.CurrentPath.CurrentNode.ConnectedDoor.Item.AiTarget)
|
||||
if (canAttackSub)
|
||||
{
|
||||
SelectTarget(pathSteering.CurrentPath.CurrentNode.ConnectedDoor.Item.AiTarget);
|
||||
return;
|
||||
// If the target is in the same hull, there shouldn't be any doors blocking the path
|
||||
if (targetCharacter == null || targetCharacter.CurrentHull != Character.CurrentHull)
|
||||
{
|
||||
var door = pathSteering.CurrentPath.CurrentNode?.ConnectedDoor ?? pathSteering.CurrentPath.NextNode?.ConnectedDoor;
|
||||
if (door != null && !door.IsOpen && door.Item.Condition > 0)
|
||||
{
|
||||
if (SelectedAiTarget != door.Item.AiTarget)
|
||||
{
|
||||
SelectTarget(door.Item.AiTarget, selectedTargetMemory.Priority);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (canAttackSub && pathSteering.CurrentPath.NextNode?.ConnectedDoor != null && SelectedAiTarget != pathSteering.CurrentPath.NextNode.ConnectedDoor.Item.AiTarget)
|
||||
// Steer towards the target if in the same room and swimming
|
||||
if ((Character.AnimController.InWater || pursue) && targetCharacter != null && VisibleHulls.Contains(targetCharacter.CurrentHull))
|
||||
{
|
||||
SelectTarget(pathSteering.CurrentPath.NextNode.ConnectedDoor.Item.AiTarget);
|
||||
return;
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(attackSimPos - steeringLimb.SimPosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Steer towards the target if in the same room and swimming
|
||||
if (Character.AnimController.InWater && targetCharacter != null && VisibleHulls.Contains(targetCharacter.CurrentHull))
|
||||
SteeringManager.SteeringSeek(steerPos, 2);
|
||||
// Switch to Idle when cannot reach the target and if cannot damage the walls
|
||||
if ((!canAttackSub || wallTarget == null) && !pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(attackSimPos - steeringLimb.SimPosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringSeek(steerPos, 2);
|
||||
// Switch to Idle when cannot reach the target and if cannot damage the walls
|
||||
if ((!canAttackSub || wallTarget == null) && !pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
}
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1148,8 +1154,8 @@ namespace Barotrauma
|
||||
{
|
||||
selectedTargetMemory.Priority = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -525,6 +525,7 @@ namespace Barotrauma
|
||||
protected void ReportProblems()
|
||||
{
|
||||
Order newOrder = null;
|
||||
Hull targetHull = null;
|
||||
if (Character.CurrentHull != null)
|
||||
{
|
||||
foreach (var hull in VisibleHulls)
|
||||
@@ -534,21 +535,21 @@ namespace Barotrauma
|
||||
if (c.CurrentHull != hull || !c.Enabled) { continue; }
|
||||
if (AIObjectiveFightIntruders.IsValidTarget(c, Character))
|
||||
{
|
||||
AddTargets<AIObjectiveFightIntruders, Character>(Character, c);
|
||||
if (newOrder == null)
|
||||
if (AddTargets<AIObjectiveFightIntruders, Character>(Character, c) && newOrder == null)
|
||||
{
|
||||
var orderPrefab = Order.GetPrefab("reportintruders");
|
||||
newOrder = new Order(orderPrefab, c.CurrentHull, null, orderGiver: Character);
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AIObjectiveExtinguishFires.IsValidTarget(hull, Character))
|
||||
{
|
||||
AddTargets<AIObjectiveExtinguishFires, Hull>(Character, hull);
|
||||
if (newOrder == null)
|
||||
if (AddTargets<AIObjectiveExtinguishFires, Hull>(Character, hull) && newOrder == null)
|
||||
{
|
||||
var orderPrefab = Order.GetPrefab("reportfire");
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
}
|
||||
foreach (Character c in Character.CharacterList)
|
||||
@@ -556,13 +557,11 @@ namespace Barotrauma
|
||||
if (c.CurrentHull != hull) { continue; }
|
||||
if (AIObjectiveRescueAll.IsValidTarget(c, Character))
|
||||
{
|
||||
if (AddTargets<AIObjectiveRescueAll, Character>(c, Character))
|
||||
if (AddTargets<AIObjectiveRescueAll, Character>(c, Character) && newOrder == null && !ObjectiveManager.HasActiveObjective<AIObjectiveRescue>())
|
||||
{
|
||||
if (newOrder == null)
|
||||
{
|
||||
var orderPrefab = Order.GetPrefab("requestfirstaid");
|
||||
newOrder = new Order(orderPrefab, c.CurrentHull, null, orderGiver: Character);
|
||||
}
|
||||
var orderPrefab = Order.GetPrefab("requestfirstaid");
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -570,11 +569,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (AIObjectiveFixLeaks.IsValidTarget(gap, Character))
|
||||
{
|
||||
AddTargets<AIObjectiveFixLeaks, Gap>(Character, gap);
|
||||
if (newOrder == null && !gap.IsRoomToRoom)
|
||||
if (AddTargets<AIObjectiveFixLeaks, Gap>(Character, gap) && newOrder == null && !gap.IsRoomToRoom)
|
||||
{
|
||||
var orderPrefab = Order.GetPrefab("reportbreach");
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -584,11 +583,11 @@ namespace Barotrauma
|
||||
if (AIObjectiveRepairItems.IsValidTarget(item, Character))
|
||||
{
|
||||
if (item.Repairables.All(r => item.ConditionPercentage > r.ShowRepairUIThreshold)) { continue; }
|
||||
AddTargets<AIObjectiveRepairItems, Item>(Character, item);
|
||||
if (newOrder == null)
|
||||
if (AddTargets<AIObjectiveRepairItems, Item>(Character, item) && newOrder == null && !ObjectiveManager.HasActiveObjective<AIObjectiveRepairItem>())
|
||||
{
|
||||
var orderPrefab = Order.GetPrefab("reportbrokendevices");
|
||||
newOrder = new Order(orderPrefab, item.CurrentHull, item.Repairables?.FirstOrDefault(), orderGiver: Character);
|
||||
newOrder = new Order(orderPrefab, hull, item.Repairables?.FirstOrDefault(), orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -598,9 +597,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.AddOrder(newOrder, newOrder.FadeOutTime))
|
||||
{
|
||||
Character.Speak(newOrder.GetChatMessage("", Character.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order);
|
||||
Character.Speak(newOrder.GetChatMessage("", targetHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order);
|
||||
#if SERVER
|
||||
GameMain.Server.SendOrderChatMessage(new OrderChatMessage(newOrder, "", Character.CurrentHull, null, Character));
|
||||
GameMain.Server.SendOrderChatMessage(new OrderChatMessage(newOrder, "", targetHull, null, Character));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,8 @@ namespace Barotrauma
|
||||
{
|
||||
diff.Y = 0.0f;
|
||||
}
|
||||
if (diff.LengthSquared() < 0.001f) { return -host.Steering; }
|
||||
//if (diff.LengthSquared() < 0.001f) { return -host.Steering; }
|
||||
if (diff == Vector2.Zero) { return Vector2.Zero; }
|
||||
return Vector2.Normalize(diff) * weight;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Barotrauma
|
||||
foreach (FireSource fs in targetHull.FireSources)
|
||||
{
|
||||
bool inRange = fs.IsInDamageRange(character, MathHelper.Clamp(fs.DamageRange * 1.5f, extinguisher.Range * 0.5f, extinguisher.Range));
|
||||
bool move = !inRange;
|
||||
bool move = !inRange || !HumanAIController.VisibleHulls.Contains(fs.Hull);
|
||||
if (inRange || useExtinquisherTimer > 0.0f)
|
||||
{
|
||||
useExtinquisherTimer += deltaTime;
|
||||
@@ -79,7 +79,6 @@ namespace Barotrauma
|
||||
{
|
||||
useExtinquisherTimer = 0.0f;
|
||||
}
|
||||
character.AIController.SteeringManager.Reset();
|
||||
character.CursorPosition = fs.Position;
|
||||
if (extinguisher.Item.RequireAimToUse)
|
||||
{
|
||||
@@ -106,20 +105,19 @@ namespace Barotrauma
|
||||
{
|
||||
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
|
||||
}
|
||||
if (!character.CanSeeTarget(fs, sightLimb))
|
||||
if (character.CanSeeTarget(fs, sightLimb))
|
||||
{
|
||||
move = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
move = false;
|
||||
character.SetInput(extinguisher.Item.IsShootable ? InputType.Shoot : InputType.Use, false, true);
|
||||
extinguisher.Use(deltaTime, character);
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogPutOutFire", "[roomname]", targetHull.Name, true), null, 0, "putoutfire", 10.0f);
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogPutOutFire", "[roomname]", targetHull.RoomName, true), null, 0, "putoutfire", 10.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
move = true;
|
||||
}
|
||||
}
|
||||
if (move)
|
||||
{
|
||||
@@ -128,6 +126,10 @@ namespace Barotrauma
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref gotoObjective));
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma
|
||||
{
|
||||
public override string DebugTag => "fight intruders";
|
||||
protected override float IgnoreListClearInterval => 30;
|
||||
public virtual bool IgnoreUnsafeHulls => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier) { }
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace Barotrauma
|
||||
{
|
||||
SteeringManager.SteeringSeek(character.GetRelativeSimPosition(Target), 10);
|
||||
}
|
||||
if (!insideSteering)
|
||||
if (!insideSteering && character.CurrentHull == null)
|
||||
{
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 1);
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ namespace Barotrauma
|
||||
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, priorityModifier: priorityModifier)
|
||||
{
|
||||
IsLoop = true,
|
||||
// Don't override auto pilot unless it's an order by a player
|
||||
// Don't override unless it's an order by a player
|
||||
Override = orderGiver == Character.Controlled || orderGiver.IsRemotePlayer
|
||||
};
|
||||
break;
|
||||
@@ -302,7 +302,7 @@ namespace Barotrauma
|
||||
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, priorityModifier: priorityModifier)
|
||||
{
|
||||
IsLoop = true,
|
||||
// Don't override auto control unless it's an order by a player
|
||||
// Don't override unless it's an order by a player
|
||||
Override = orderGiver == Character.Controlled || orderGiver.IsRemotePlayer
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace Barotrauma
|
||||
public Entity OperateTarget => operateTarget;
|
||||
public ItemComponent Component => component;
|
||||
|
||||
public ItemComponent GetTarget() => useController ? controller : component;
|
||||
|
||||
public Func<bool> completionCondition;
|
||||
|
||||
public override float GetPriority()
|
||||
@@ -35,6 +37,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (component.Item.CurrentHull == null) { return 0; }
|
||||
if (component.Item.CurrentHull.FireSources.Count > 0) { return 0; }
|
||||
if (IsOperatedByAnother(GetTarget())) { return 0; }
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == component.Item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return 0; }
|
||||
float devotion = MathHelper.Min(10, Priority);
|
||||
float value = devotion + AIObjectiveManager.OrderPriority * PriorityModifier;
|
||||
@@ -58,6 +61,45 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsOperatedByAnother(ItemComponent target)
|
||||
{
|
||||
foreach (var c in Character.CharacterList)
|
||||
{
|
||||
if (c == character) { continue; }
|
||||
if (!HumanAIController.IsFriendly(c)) { continue; }
|
||||
if (c.SelectedConstruction != target.Item) { continue; }
|
||||
// If the other character is player, don't try to operate
|
||||
if (c.IsRemotePlayer || Character.Controlled == c) { return true; }
|
||||
if (c.AIController is HumanAIController humanAi)
|
||||
{
|
||||
// If the other character is ordered to operate the item, let him do it
|
||||
if (humanAi.ObjectiveManager.IsCurrentOrder<AIObjectiveOperateItem>())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target is Steering)
|
||||
{
|
||||
// Steering is hard-coded -> cannot use the required skills collection defined in the xml
|
||||
return character.GetSkillLevel("helm") <= c.GetSkillLevel("helm");
|
||||
}
|
||||
else
|
||||
{
|
||||
return target.DegreeOfSuccess(character) <= target.DegreeOfSuccess(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shouldn't go here, unless we allow non-humans to operate items
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
if (character.LockHands)
|
||||
@@ -65,24 +107,24 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
ItemComponent target = useController ? controller : component;
|
||||
ItemComponent target = GetTarget();
|
||||
if (useController && controller == null)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogCantFindController", "[item]", component.Item.Name, true), null, 2.0f, "cantfindcontroller", 30.0f);
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
// Don't allow to operate an item that someone with a better skills already operates, unless this is an order
|
||||
if (objectiveManager.CurrentOrder != this && IsOperatedByAnother(target))
|
||||
{
|
||||
// Don't abandon
|
||||
return;
|
||||
}
|
||||
if (target.CanBeSelected)
|
||||
{
|
||||
if (character.CanInteractWith(target.Item, out _, checkLinked: false))
|
||||
{
|
||||
HumanAIController.FaceTarget(target.Item);
|
||||
// Don't allow to operate an item that someone already operates, unless this objective is an order
|
||||
if (objectiveManager.CurrentOrder != this && Character.CharacterList.Any(c => c.SelectedConstruction == target.Item && c != character && HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)))
|
||||
{
|
||||
// Don't abandon
|
||||
return;
|
||||
}
|
||||
if (character.SelectedConstruction != target.Item)
|
||||
{
|
||||
target.Item.TryInteract(character, false, true);
|
||||
|
||||
@@ -764,12 +764,17 @@ namespace Barotrauma
|
||||
.Where(p => p.AfflictionType == "huskinfection")
|
||||
.Select(p => p as AfflictionPrefabHusk)
|
||||
.FirstOrDefault(p => p.TargetSpecies.Any(t => t.Equals(AfflictionHusk.GetNonHuskedSpeciesName(speciesName, p), StringComparison.InvariantCultureIgnoreCase)));
|
||||
string nonHuskedSpeciesName = string.Empty;
|
||||
if (matchingAffliction == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Cannot find a husk infection that matches this species! Please add the speciesnames as 'targets' in the husk affliction prefab definition!");
|
||||
return;
|
||||
// Crashes if we fail to create a ragdoll -> Let's just use some ragdoll so that the user sees the error msg.
|
||||
nonHuskedSpeciesName = IsHumanoid ? HumanSpeciesName : "crawler";
|
||||
}
|
||||
else
|
||||
{
|
||||
nonHuskedSpeciesName = AfflictionHusk.GetNonHuskedSpeciesName(speciesName, matchingAffliction);
|
||||
}
|
||||
string nonHuskedSpeciesName = AfflictionHusk.GetNonHuskedSpeciesName(speciesName, matchingAffliction);
|
||||
ragdollParams = IsHumanoid ? RagdollParams.GetDefaultRagdollParams<HumanRagdollParams>(nonHuskedSpeciesName) : RagdollParams.GetDefaultRagdollParams<FishRagdollParams>(nonHuskedSpeciesName) as RagdollParams;
|
||||
if (info == null)
|
||||
{
|
||||
@@ -1912,7 +1917,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
focusedCharacter = FindCharacterAtPosition(mouseSimPos);
|
||||
focusedCharacter = CanInteract ? FindCharacterAtPosition(mouseSimPos) : null;
|
||||
focusedItem = CanInteract ?
|
||||
FindItemAtPosition(mouseSimPos, GameMain.Config.AimAssistAmount * (AnimController.InWater ? 1.5f : 1.0f)) : null;
|
||||
findFocusedTimer = 0.05f;
|
||||
@@ -1972,11 +1977,11 @@ namespace Barotrauma
|
||||
{
|
||||
DeselectCharacter();
|
||||
}
|
||||
else if (focusedCharacter != null && IsKeyHit(InputType.Grab) && FocusedCharacter.CanBeDragged)
|
||||
else if (focusedCharacter != null && IsKeyHit(InputType.Grab) && FocusedCharacter.CanBeDragged && CanInteract)
|
||||
{
|
||||
SelectCharacter(focusedCharacter);
|
||||
}
|
||||
else if (focusedCharacter != null && IsKeyHit(InputType.Health) && focusedCharacter.CharacterHealth.UseHealthWindow && CanInteractWith(focusedCharacter, 160f, false))
|
||||
else if (focusedCharacter != null && IsKeyHit(InputType.Health) && focusedCharacter.CharacterHealth.UseHealthWindow && CanInteract && CanInteractWith(focusedCharacter, 160f, false))
|
||||
{
|
||||
if (focusedCharacter == SelectedCharacter)
|
||||
{
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace Barotrauma
|
||||
public static string GetNonHuskedSpeciesName(string huskedSpeciesName, AfflictionPrefabHusk prefab)
|
||||
{
|
||||
string nonTag = prefab.HuskedSpeciesName.Remove(AfflictionPrefabHusk.Tag);
|
||||
return huskedSpeciesName.Remove(nonTag);
|
||||
return huskedSpeciesName.ToLowerInvariant().Remove(nonTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Barotrauma
|
||||
{
|
||||
public AfflictionPrefabHusk(XElement element, Type type = null) : base(element, type)
|
||||
{
|
||||
HuskedSpeciesName = element.GetAttributeString("huskedspeciesname", null);
|
||||
HuskedSpeciesName = element.GetAttributeString("huskedspeciesname", null).ToLowerInvariant();
|
||||
if (HuskedSpeciesName == null)
|
||||
{
|
||||
DebugConsole.NewMessage($"No 'huskedspeciesname' defined for the husk affliction ({Identifier}) in {element.ToString()}", Color.Orange);
|
||||
|
||||
@@ -85,7 +85,11 @@ namespace Barotrauma
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (IsClient) { return; }
|
||||
if (IsClient)
|
||||
{
|
||||
if (item.ParentInventory != null) { item.body.FarseerBody.IsKinematic = false; }
|
||||
return;
|
||||
}
|
||||
switch (State)
|
||||
{
|
||||
case 0:
|
||||
|
||||
@@ -296,10 +296,11 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
#if DEBUG
|
||||
return false;
|
||||
/*#if DEBUG
|
||||
return false;
|
||||
#endif
|
||||
return sendUserStatistics;
|
||||
return sendUserStatistics;*/
|
||||
}
|
||||
set
|
||||
{
|
||||
|
||||
@@ -25,7 +25,18 @@ namespace Barotrauma.Items.Components
|
||||
private readonly bool autoOrientGap;
|
||||
|
||||
private bool isStuck;
|
||||
public bool IsStuck => isStuck;
|
||||
public bool IsStuck
|
||||
{
|
||||
get { return isStuck; }
|
||||
private set
|
||||
{
|
||||
if (isStuck == value) { return; }
|
||||
isStuck = value;
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private float resetPredictionTimer;
|
||||
|
||||
@@ -65,12 +76,12 @@ namespace Barotrauma.Items.Components
|
||||
public float Stuck
|
||||
{
|
||||
get { return stuck; }
|
||||
set
|
||||
set
|
||||
{
|
||||
if (isOpen || isBroken || !CanBeWelded) return;
|
||||
stuck = MathHelper.Clamp(value, 0.0f, 100.0f);
|
||||
if (stuck <= 0.0f) isStuck = false;
|
||||
if (stuck >= 100.0f) isStuck = true;
|
||||
if (stuck <= 0.0f) { IsStuck = false; }
|
||||
if (stuck >= 100.0f) { IsStuck = true; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +307,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
bool isClosing = false;
|
||||
if (!isStuck)
|
||||
if (!IsStuck)
|
||||
{
|
||||
if (PredictedState == null)
|
||||
{
|
||||
@@ -541,7 +552,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
if (isStuck) return;
|
||||
if (IsStuck) return;
|
||||
|
||||
bool wasOpen = PredictedState == null ? isOpen : PredictedState.Value;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class ElectricalDischarger : Powered
|
||||
{
|
||||
private static List<ElectricalDischarger> list = new List<ElectricalDischarger>();
|
||||
private static readonly List<ElectricalDischarger> list = new List<ElectricalDischarger>();
|
||||
public static IEnumerable<ElectricalDischarger> List
|
||||
{
|
||||
get { return list; }
|
||||
@@ -48,14 +48,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(100.0f, true, description: "How far the discharge can travel from the item."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)]
|
||||
[Serialize(500.0f, true, description: "How far the discharge can travel from the item."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)]
|
||||
public float Range
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(10.0f, true, description: "How much further can the discharge be carried when moving across walls."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
[Serialize(25.0f, true, description: "How much further can the discharge be carried when moving across walls."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
public float RangeMultiplierInWalls
|
||||
{
|
||||
get;
|
||||
@@ -127,20 +127,42 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
frameOffset = Rand.Int(electricitySprite.FrameCount);
|
||||
#endif
|
||||
if (timer > 0.0f)
|
||||
{
|
||||
if (charging)
|
||||
{
|
||||
if (Voltage > MinVoltage)
|
||||
{
|
||||
Discharge();
|
||||
}
|
||||
}
|
||||
timer -= deltaTime;
|
||||
}
|
||||
else
|
||||
if (timer <= 0.0f)
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
timer -= deltaTime;
|
||||
if (charging)
|
||||
{
|
||||
if (GetAvailableBatteryPower() >= powerConsumption)
|
||||
{
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
float neededPower = powerConsumption;
|
||||
while (neededPower > 0.0001f && batteries.Count > 0)
|
||||
{
|
||||
batteries.RemoveAll(b => b.Charge <= 0.0001f || b.MaxOutPut <= 0.0001f);
|
||||
float takePower = neededPower / batteries.Count;
|
||||
takePower = Math.Min(takePower, batteries.Min(b => Math.Min(b.Charge * 3600.0f, b.MaxOutPut)));
|
||||
foreach (PowerContainer battery in batteries)
|
||||
{
|
||||
neededPower -= takePower;
|
||||
battery.Charge -= takePower / 3600.0f;
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
battery.Item.CreateServerEvent(battery);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Discharge();
|
||||
}
|
||||
else if (Voltage > MinVoltage)
|
||||
{
|
||||
Discharge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace Barotrauma.Items.Components
|
||||
//TODO: refactor the hitting logic (get rid of the magic numbers, make it possible to use different kinds of animations for different items)
|
||||
if (!hitting)
|
||||
{
|
||||
bool aim = picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && (picker.SelectedConstruction == null || picker.SelectedConstruction.GetComponent<Ladder>() != null);
|
||||
bool aim = picker.AllowInput && picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && (picker.SelectedConstruction == null || picker.SelectedConstruction.GetComponent<Ladder>() != null);
|
||||
if (aim)
|
||||
{
|
||||
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 5f, MathHelper.PiOver4));
|
||||
|
||||
@@ -96,11 +96,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
useState -= deltaTime;
|
||||
|
||||
if (useState <= 0.0f) IsActive = false;
|
||||
|
||||
if (item.AiTarget != null)
|
||||
if (useState <= 0.0f)
|
||||
{
|
||||
item.AiTarget.SoundRange = IsActive ? item.AiTarget.MaxSoundRange : item.AiTarget.MinSoundRange;
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
if (item.AiTarget != null && IsActive)
|
||||
{
|
||||
item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -399,7 +399,7 @@ namespace Barotrauma.Items.Components
|
||||
case "activate":
|
||||
case "use":
|
||||
case "trigger_in":
|
||||
item.Use(1.0f);
|
||||
item.Use(1.0f, sender);
|
||||
break;
|
||||
case "toggle":
|
||||
if (signal != "0")
|
||||
@@ -669,7 +669,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Character user = null)
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Entity useTarget = null, Character user = null, Vector2? worldPosition = null)
|
||||
{
|
||||
if (statusEffectLists == null) return;
|
||||
|
||||
@@ -680,20 +680,24 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (broken && effect.type != ActionType.OnBroken) { continue; }
|
||||
if (user != null) { effect.SetUser(user); }
|
||||
item.ApplyStatusEffect(effect, type, deltaTime, character, targetLimb, false, false);
|
||||
item.ApplyStatusEffect(effect, type, deltaTime, character, targetLimb, useTarget, false, false, worldPosition);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(XElement componentElement, bool usePrefabValues)
|
||||
{
|
||||
if (componentElement == null || usePrefabValues) { return; }
|
||||
foreach (XAttribute attribute in componentElement.Attributes())
|
||||
{
|
||||
if (!SerializableProperties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) continue;
|
||||
property.TrySetValue(this, attribute.Value);
|
||||
if (componentElement != null && !usePrefabValues)
|
||||
{
|
||||
foreach (XAttribute attribute in componentElement.Attributes())
|
||||
{
|
||||
if (!SerializableProperties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) continue;
|
||||
property.TrySetValue(this, attribute.Value);
|
||||
}
|
||||
ParseMsg();
|
||||
OverrideRequiredItems(componentElement);
|
||||
}
|
||||
ParseMsg();
|
||||
OverrideRequiredItems(componentElement);
|
||||
|
||||
if (item.Submarine != null) { SerializableProperty.UpgradeGameVersion(this, originalElement, item.Submarine.GameVersion); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -123,9 +123,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (user.AnimController.InWater)
|
||||
{
|
||||
if (diff.Length() > 30.0f)
|
||||
if (diff.LengthSquared() > 30.0f * 30.0f)
|
||||
{
|
||||
user.AnimController.TargetMovement = Vector2.Clamp(diff*0.01f, -Vector2.One, Vector2.One);
|
||||
user.AnimController.TargetMovement = Vector2.Clamp(diff * 0.01f, -Vector2.One, Vector2.One);
|
||||
user.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
|
||||
}
|
||||
else
|
||||
@@ -136,11 +136,29 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
diff.Y = 0.0f;
|
||||
if (diff != Vector2.Zero && diff.LengthSquared() > 10.0f * 10.0f)
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && user != Character.Controlled)
|
||||
{
|
||||
user.AnimController.TargetMovement = Vector2.Normalize(diff);
|
||||
user.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
|
||||
return;
|
||||
if (Math.Abs(diff.X) > 20.0f)
|
||||
{
|
||||
//wait for the character to walk to the correct position
|
||||
return;
|
||||
}
|
||||
else if (Math.Abs(diff.X) > 0.1f)
|
||||
{
|
||||
//aim to keep the collider at the correct position once close enough
|
||||
user.AnimController.Collider.LinearVelocity = new Vector2(
|
||||
diff.X * 0.1f,
|
||||
user.AnimController.Collider.LinearVelocity.Y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Math.Abs(diff.X) > 10.0f)
|
||||
{
|
||||
user.AnimController.TargetMovement = Vector2.Normalize(diff);
|
||||
user.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
|
||||
return;
|
||||
}
|
||||
}
|
||||
user.AnimController.TargetMovement = Vector2.Zero;
|
||||
}
|
||||
@@ -293,7 +311,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void CancelUsing(Character character)
|
||||
{
|
||||
if (character == null || character.Removed) return;
|
||||
if (character == null || character.Removed) { return; }
|
||||
|
||||
foreach (LimbPos lb in limbPositions)
|
||||
{
|
||||
@@ -304,18 +322,21 @@ namespace Barotrauma.Items.Components
|
||||
limb.PullJointEnabled = false;
|
||||
}
|
||||
|
||||
if (character.SelectedConstruction == this.item) character.SelectedConstruction = null;
|
||||
if (character.SelectedConstruction == this.item) { character.SelectedConstruction = null; }
|
||||
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
if (character == Character.Controlled)
|
||||
{
|
||||
HideHUDs(false);
|
||||
}
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
public override bool Select(Character activator)
|
||||
{
|
||||
if (activator == null || activator.Removed) return false;
|
||||
if (activator == null || activator.Removed) { return false; }
|
||||
|
||||
//someone already using the item
|
||||
if (user != null && !user.Removed)
|
||||
@@ -330,10 +351,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
user = activator;
|
||||
user = activator;
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
item.SendSignal(0, "1", "signal_out", user);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.CurrentHull == null) { return; }
|
||||
|
||||
float powerFactor = currPowerConsumption <= 0.0f ? 1.0f : Voltage;
|
||||
float powerFactor = Math.Min(currPowerConsumption <= 0.0f ? 1.0f : Voltage, 1.0f);
|
||||
|
||||
currFlow = flowPercentage / 100.0f * maxFlow * powerFactor;
|
||||
//less effective when in a bad condition
|
||||
|
||||
@@ -640,9 +640,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (lastUser != character && lastUser != null && lastUser.SelectedConstruction == item)
|
||||
if (objective.Override)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
|
||||
if (lastUser != null && lastUser != character && lastUser != lastAIUser)
|
||||
{
|
||||
if (lastUser.SelectedConstruction == item)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LastUser = lastAIUser = character;
|
||||
|
||||
@@ -479,9 +479,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
if (user != character && user != null && user.SelectedConstruction == item)
|
||||
if (objective.Override)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
|
||||
if (user != character && user != null && user.SelectedConstruction == item)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
|
||||
}
|
||||
}
|
||||
user = character;
|
||||
if (!AutoPilot)
|
||||
|
||||
@@ -175,9 +175,8 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
currPowerConsumption = MathHelper.Lerp(currPowerConsumption, rechargeSpeed, 0.05f);
|
||||
Charge += currPowerConsumption * Voltage / 3600.0f;
|
||||
}
|
||||
|
||||
Charge += currPowerConsumption * Math.Min(Voltage, 1.0f) / 3600.0f;
|
||||
}
|
||||
|
||||
if (charge <= 0.0f)
|
||||
{
|
||||
|
||||
@@ -280,6 +280,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
//we've already received this signal
|
||||
if (lastPowerProbeRecipients.Contains(this)) { return; }
|
||||
if (item.Condition <= 0.0f) { return; }
|
||||
|
||||
lastPowerProbeRecipients.Add(this);
|
||||
|
||||
if (power < 0.0f)
|
||||
@@ -306,15 +308,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (ItemComponent ic in recipient.Item.Components)
|
||||
{
|
||||
//powertransfer components don't need to receive the signal in the pass-through signal connections
|
||||
//other junction boxes don't need to receive the signal in the pass-through signal connections
|
||||
//because we relay it straight to the connected items without going through the whole chain of junction boxes
|
||||
if (ic is PowerTransfer && connection.Name.Contains("signal")) { continue; }
|
||||
if (ic is PowerTransfer && !(ic is RelayComponent) && connection.Name.Contains("signal")) { continue; }
|
||||
ic.ReceiveSignal(stepsTaken, signal, recipient, source, sender, 0.0f, signalStrength);
|
||||
}
|
||||
|
||||
foreach (StatusEffect effect in recipient.Effects)
|
||||
{
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f, null, null, false, false);
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,13 +279,30 @@ namespace Barotrauma.Items.Components
|
||||
var pc = powerSource.Item.GetComponent<PowerContainer>();
|
||||
if (pc != null)
|
||||
{
|
||||
float voltage = -pc.CurrPowerOutput / Math.Max(powered.CurrPowerConsumption, 1.0f);
|
||||
float voltage = pc.CurrPowerOutput / Math.Max(powered.CurrPowerConsumption, 1.0f);
|
||||
powered.voltage += voltage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the amount of power that can be supplied by batteries directly connected to the item
|
||||
/// </summary>
|
||||
protected float GetAvailableBatteryPower()
|
||||
{
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
|
||||
float availablePower = 0.0f;
|
||||
foreach (PowerContainer battery in batteries)
|
||||
{
|
||||
float batteryPower = Math.Min(battery.Charge * 3600.0f, battery.MaxOutPut);
|
||||
availablePower += batteryPower;
|
||||
}
|
||||
|
||||
return availablePower;
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
poweredList.Remove(this);
|
||||
|
||||
@@ -458,6 +458,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (character != null) { character.LastDamageSource = item; }
|
||||
|
||||
#if CLIENT
|
||||
PlaySound(ActionType.OnUse, item.WorldPosition, user: user);
|
||||
PlaySound(ActionType.OnImpact, item.WorldPosition, user: user);
|
||||
#endif
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
if (target.Body.UserData is Limb targetLimb)
|
||||
@@ -489,14 +494,26 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if SERVER
|
||||
if (GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnUse });
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnImpact });
|
||||
}
|
||||
if (GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnUse, this, targetLimb.character.ID, targetLimb, (ushort)0, item.WorldPosition });
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnImpact, this, targetLimb.character.ID, targetLimb, (ushort)0, item.WorldPosition });
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, useTarget: target.Body.UserData as Entity, user: user);
|
||||
ApplyStatusEffects(ActionType.OnImpact, 1.0f, useTarget: target.Body.UserData as Entity, user: user);
|
||||
#if SERVER
|
||||
if (GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnUse, this, (ushort)0, null, (target.Body.UserData as Entity)?.ID ?? 0, item.WorldPosition });
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnImpact, this, (ushort)0, null, (target.Body.UserData as Entity)?.ID ?? 0, item.WorldPosition });
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
|
||||
|
||||
@@ -130,6 +130,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
#if SERVER
|
||||
if (CurrentFixer != character || currentFixerAction != action)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#endif
|
||||
CurrentFixer = character;
|
||||
CurrentFixerAction = action;
|
||||
return true;
|
||||
@@ -140,12 +146,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (CurrentFixer == character)
|
||||
{
|
||||
#if SERVER
|
||||
if (CurrentFixer != character || currentFixerAction != FixActions.None)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#endif
|
||||
CurrentFixer.AnimController.Anim = AnimController.Animation.None;
|
||||
CurrentFixer = null;
|
||||
currentFixerAction = FixActions.None;
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
#if CLIENT
|
||||
repairSoundChannel?.FadeOutAndDispose();
|
||||
repairSoundChannel = null;
|
||||
@@ -214,16 +223,16 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateFixAnimation(CurrentFixer);
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
|
||||
if (CurrentFixer != null && (CurrentFixer.SelectedConstruction != item || !CurrentFixer.CanInteractWith(item) || CurrentFixer.IsDead))
|
||||
{
|
||||
StopRepairing(CurrentFixer);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateFixAnimation(CurrentFixer);
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
|
||||
float successFactor = requiredSkills.Count == 0 ? 1.0f : DegreeOfSuccess(CurrentFixer, requiredSkills);
|
||||
|
||||
//item must have been below the repair threshold for the player to get an achievement or XP for repairing it
|
||||
|
||||
@@ -258,7 +258,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (StatusEffect effect in recipient.Effects)
|
||||
{
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step, null, null, false, false);
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,11 @@ namespace Barotrauma.Items.Components
|
||||
set { /*do nothing*/ }
|
||||
}
|
||||
|
||||
public Character User
|
||||
{
|
||||
get { return user; }
|
||||
}
|
||||
|
||||
public ConnectionPanel(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -126,6 +131,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (user == null || user.SelectedConstruction != item)
|
||||
{
|
||||
#if SERVER
|
||||
if (user != null) { item.CreateServerEvent(this); }
|
||||
#endif
|
||||
user = null;
|
||||
return;
|
||||
}
|
||||
@@ -135,6 +143,11 @@ namespace Barotrauma.Items.Components
|
||||
user.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * (((float)Timing.TotalTime / 10.0f) % 0.1f));
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
Update(deltaTime, cam);
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public override bool Select(Character picker)
|
||||
@@ -147,13 +160,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
user = picker;
|
||||
#if SERVER
|
||||
if (user != null) { item.CreateServerEvent(this); }
|
||||
#endif
|
||||
IsActive = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || character != user) return false;
|
||||
if (character == null || character != user) { return false; }
|
||||
|
||||
var powered = item.GetComponent<Powered>();
|
||||
if (powered != null)
|
||||
@@ -257,6 +273,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
{
|
||||
#if CLIENT
|
||||
TriggerRewiringSound();
|
||||
#endif
|
||||
|
||||
foreach (Connection connection in Connections)
|
||||
{
|
||||
foreach (Wire wire in connection.Wires)
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (StatusEffect effect in ciElement.StatusEffects)
|
||||
{
|
||||
item.ApplyStatusEffect(effect, ciElement.State ? ActionType.OnUse : ActionType.OnSecondaryUse, 1.0f, null, null, true, false);
|
||||
item.ApplyStatusEffect(effect, ciElement.State ? ActionType.OnUse : ActionType.OnSecondaryUse, 1.0f, null, null, null, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
range = MathHelper.Clamp(value, 0.0f, 4096.0f);
|
||||
#if CLIENT
|
||||
if (light != null) light.Range = range;
|
||||
if (light != null) { light.Range = range; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return IsActive; }
|
||||
set
|
||||
{
|
||||
if (IsActive == value) return;
|
||||
if (IsActive == value) { return; }
|
||||
|
||||
IsActive = value;
|
||||
#if SERVER
|
||||
@@ -135,11 +135,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (base.IsActive == value) { return; }
|
||||
base.IsActive = value;
|
||||
#if CLIENT
|
||||
if (light == null) return;
|
||||
light.Color = value ? lightColor : Color.Transparent;
|
||||
if (!value) lightBrightness = 0.0f;
|
||||
#endif
|
||||
|
||||
SetLightSourceState(value, value ? lightBrightness : 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +150,8 @@ namespace Barotrauma.Items.Components
|
||||
Position = item.Position,
|
||||
CastShadows = castShadows,
|
||||
IsBackground = drawBehindSubs,
|
||||
SpriteScale = Vector2.One * item.Scale
|
||||
SpriteScale = Vector2.One * item.Scale,
|
||||
Range = range
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -161,40 +159,34 @@ namespace Barotrauma.Items.Components
|
||||
item.AddTag("light");
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
public override void OnScaleChanged()
|
||||
{
|
||||
light.SpriteScale = Vector2.One * item.Scale;
|
||||
light.Position = ParentBody != null ? ParentBody.Position : item.Position;
|
||||
}
|
||||
#endif
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
itemLoaded = true;
|
||||
#if CLIENT
|
||||
light.Color = IsActive ? lightColor : Color.Transparent;
|
||||
if (!IsActive) lightBrightness = 0.0f;
|
||||
#endif
|
||||
SetLightSourceState(IsActive, lightBrightness);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
UpdateAITarget(item.AiTarget);
|
||||
}
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
|
||||
#if CLIENT
|
||||
light.ParentSub = item.Submarine;
|
||||
#endif
|
||||
if (item.Container != null)
|
||||
{
|
||||
light.Color = Color.Transparent;
|
||||
SetLightSourceState(false, 0.0f);
|
||||
return;
|
||||
}
|
||||
#if CLIENT
|
||||
light.Position = ParentBody != null ? ParentBody.Position : item.Position;
|
||||
#endif
|
||||
|
||||
PhysicsBody body = ParentBody ?? item.body;
|
||||
|
||||
if (body != null)
|
||||
{
|
||||
#if CLIENT
|
||||
@@ -203,9 +195,7 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
if (!body.Enabled)
|
||||
{
|
||||
#if CLIENT
|
||||
light.Color = Color.Transparent;
|
||||
#endif
|
||||
SetLightSourceState(false, 0.0f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -217,7 +207,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
|
||||
if (Rand.Range(0.0f, 1.0f) < 0.05f && Voltage < Rand.Range(0.0f, MinVoltage))
|
||||
{
|
||||
#if CLIENT
|
||||
@@ -240,36 +229,21 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (blinkTimer > 0.5f)
|
||||
{
|
||||
#if CLIENT
|
||||
light.Color = Color.Transparent;
|
||||
#endif
|
||||
SetLightSourceState(false, lightBrightness);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if CLIENT
|
||||
light.Color = lightColor * lightBrightness * (1.0f - Rand.Range(0.0f, Flicker));
|
||||
light.Range = range;
|
||||
#endif
|
||||
SetLightSourceState(true, lightBrightness * (1.0f - Rand.Range(0.0f, flicker)));
|
||||
}
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
UpdateAITarget(item.AiTarget);
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
light.Color = Color.Transparent;
|
||||
lightBrightness = 0.0f;
|
||||
|
||||
if (powerIn == null && powerConsumption > 0.0f) { Voltage -= deltaTime; }
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
light.Remove();
|
||||
SetLightSourceState(false, 0.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
return true;
|
||||
@@ -277,8 +251,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
base.ReceiveSignal(stepsTaken, signal, connection, source, sender, power, signalStrength);
|
||||
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "toggle":
|
||||
@@ -300,13 +272,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void UpdateAITarget(AITarget target)
|
||||
{
|
||||
//voltage > minVoltage || powerConsumption <= 0.0f; <- ?
|
||||
target.Enabled = IsActive;
|
||||
if (!IsActive) { return; }
|
||||
if (target.MaxSightRange <= 0)
|
||||
{
|
||||
target.MaxSightRange = Range * 5;
|
||||
}
|
||||
target.SightRange = IsActive ? target.MaxSightRange * lightBrightness : 0;
|
||||
target.SightRange = target.MaxSightRange * lightBrightness;
|
||||
}
|
||||
|
||||
partial void SetLightSourceState(bool enabled, float brightness);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void ReceivePowerProbeSignal(Connection connection, Item source, float power)
|
||||
{
|
||||
if (!IsOn) { return; }
|
||||
if (!IsOn || item.Condition <= 0.0f) { return; }
|
||||
|
||||
//we've already received this signal
|
||||
if (lastPowerProbeRecipients.Contains(this)) { return; }
|
||||
|
||||
@@ -276,7 +276,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (reload > 0.0f) return false;
|
||||
|
||||
if (GetAvailablePower() < powerConsumption)
|
||||
if (GetAvailableBatteryPower() < powerConsumption)
|
||||
{
|
||||
#if CLIENT
|
||||
if (!flashLowPower && character != null && character == Character.Controlled)
|
||||
@@ -410,7 +410,7 @@ namespace Barotrauma.Items.Components
|
||||
character.AIController.SelectTarget(null);
|
||||
}
|
||||
|
||||
if (GetAvailablePower() < powerConsumption)
|
||||
if (GetAvailableBatteryPower() < powerConsumption)
|
||||
{
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
|
||||
@@ -540,21 +540,6 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
private float GetAvailablePower()
|
||||
{
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
|
||||
float availablePower = 0.0f;
|
||||
foreach (PowerContainer battery in batteries)
|
||||
{
|
||||
float batteryPower = Math.Min(battery.Charge*3600.0f, battery.MaxOutPut);
|
||||
|
||||
availablePower += batteryPower;
|
||||
}
|
||||
|
||||
return availablePower;
|
||||
}
|
||||
|
||||
private void GetAvailablePower(out float availableCharge, out float availableCapacity)
|
||||
{
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
|
||||
@@ -425,7 +425,7 @@ namespace Barotrauma
|
||||
string[] splitTags = value.Split(',');
|
||||
foreach (string tag in splitTags)
|
||||
{
|
||||
string[] splitTag = tag.Split(':');
|
||||
string[] splitTag = tag.Trim().Split(':');
|
||||
splitTag[0] = splitTag[0].ToLowerInvariant();
|
||||
tags.Add(string.Join(":", splitTag));
|
||||
}
|
||||
@@ -1060,18 +1060,18 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb limb = null, bool isNetworkEvent = false)
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb limb = null, Entity useTarget = null, bool isNetworkEvent = false, Vector2? worldPosition = null)
|
||||
{
|
||||
if (!hasStatusEffectsOfType[(int)type]) { return; }
|
||||
foreach (StatusEffect effect in statusEffectLists[type])
|
||||
{
|
||||
ApplyStatusEffect(effect, type, deltaTime, character, limb, isNetworkEvent, false);
|
||||
ApplyStatusEffect(effect, type, deltaTime, character, limb, useTarget, isNetworkEvent, false, worldPosition);
|
||||
}
|
||||
}
|
||||
|
||||
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
|
||||
public void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character = null, Limb limb = null, bool isNetworkEvent = false, bool checkCondition = true)
|
||||
public void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character = null, Limb limb = null, Entity useTarget = null, bool isNetworkEvent = false, bool checkCondition = true, Vector2? worldPosition = null)
|
||||
{
|
||||
if (!isNetworkEvent && checkCondition)
|
||||
{
|
||||
@@ -1110,6 +1110,12 @@ namespace Barotrauma
|
||||
if (targets.Count > 0) { hasTargets = true; }
|
||||
}
|
||||
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget) && useTarget is ISerializableEntity serializableTarget)
|
||||
{
|
||||
hasTargets = true;
|
||||
targets.Add(serializableTarget);
|
||||
}
|
||||
|
||||
if (!hasTargets) { return; }
|
||||
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Hull) && CurrentHull != null)
|
||||
@@ -1125,30 +1131,32 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
if (character != null)
|
||||
{
|
||||
if (type == ActionType.OnContained && ParentInventory is CharacterInventory characterInventory)
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
targets.Add(characterInventory.Owner as ISerializableEntity);
|
||||
if (type == ActionType.OnContained && ParentInventory is CharacterInventory characterInventory)
|
||||
{
|
||||
targets.Add(characterInventory.Owner as ISerializableEntity);
|
||||
}
|
||||
else
|
||||
{
|
||||
targets.Add(character);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
||||
{
|
||||
targets.Add(character);
|
||||
targets.AddRange(character.AnimController.Limbs.ToList());
|
||||
}
|
||||
}
|
||||
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Limb))
|
||||
{
|
||||
targets.Add(limb);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
||||
{
|
||||
targets.AddRange(character.AnimController.Limbs.ToList());
|
||||
}
|
||||
|
||||
if (Container != null && effect.HasTargetType(StatusEffect.TargetType.Parent)) targets.Add(Container);
|
||||
|
||||
effect.Apply(type, deltaTime, this, targets);
|
||||
effect.Apply(type, deltaTime, this, targets, worldPosition);
|
||||
}
|
||||
|
||||
|
||||
@@ -1360,7 +1368,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (transformDirty) { return false; }
|
||||
|
||||
Vector2 normal = contact.Manifold.LocalNormal;
|
||||
contact.GetWorldManifold(out Vector2 normal, out _);
|
||||
if (contact.FixtureA.Body == f1.Body) { normal = -normal; }
|
||||
float impact = Vector2.Dot(f1.Body.LinearVelocity, -normal);
|
||||
|
||||
OnCollisionProjSpecific(f1, f2, contact, impact);
|
||||
@@ -1568,7 +1577,7 @@ namespace Barotrauma
|
||||
foreach (StatusEffect effect in connection.Effects)
|
||||
{
|
||||
if (condition <= 0.0f && effect.type != ActionType.OnBroken) { continue; }
|
||||
if (signal != "0" && !string.IsNullOrEmpty(signal)) { ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step, null, null, false, false); }
|
||||
if (signal != "0" && !string.IsNullOrEmpty(signal)) { ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step); }
|
||||
}
|
||||
connection.SendSignal(stepsTaken, signal, source ?? this, sender, power, signalStrength);
|
||||
}
|
||||
|
||||
@@ -575,6 +575,10 @@ namespace Barotrauma
|
||||
|
||||
public void ApplyFlowForces(float deltaTime, Item item)
|
||||
{
|
||||
if (item.body.Mass <= 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var gap in ConnectedGaps.Where(gap => gap.Open > 0))
|
||||
{
|
||||
var distance = MathHelper.Max(Vector2.DistanceSquared(item.Position, gap.Position) / 1000, 1f);
|
||||
|
||||
@@ -180,14 +180,22 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
linkedSub.filePath = element.GetAttributeString("filepath", "");
|
||||
int[] linkedToIds = element.GetAttributeIntArray("linkedto", new int[0]);
|
||||
int[] linkedToIds = element.GetAttributeIntArray("linkedto", new int[0]);
|
||||
for (int i = 0; i < linkedToIds.Length; i++)
|
||||
{
|
||||
linkedSub.linkedToID.Add((ushort)linkedToIds[i]);
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
if (FindEntityByID((ushort)linkedToIds[i]) is MapEntity linked)
|
||||
{
|
||||
linkedSub.linkedTo.Add(linked);
|
||||
}
|
||||
}
|
||||
}
|
||||
linkedSub.originalLinkedToID = (ushort)element.GetAttributeInt("originallinkedto", 0);
|
||||
linkedSub.originalMyPortID = (ushort)element.GetAttributeInt("originalmyport", 0);
|
||||
|
||||
|
||||
|
||||
return linkedSub.loadSub ? linkedSub : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,11 +107,14 @@ namespace Barotrauma
|
||||
{
|
||||
public readonly Entity Entity;
|
||||
|
||||
public readonly UInt16 OriginalID;
|
||||
|
||||
public readonly bool Remove = false;
|
||||
|
||||
public SpawnOrRemove(Entity entity, bool remove)
|
||||
{
|
||||
Entity = entity;
|
||||
OriginalID = entity.ID;
|
||||
Remove = remove;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +328,16 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public string ServerName;
|
||||
private string serverName;
|
||||
public string ServerName
|
||||
{
|
||||
get { return serverName; }
|
||||
set
|
||||
{
|
||||
serverName = value;
|
||||
if (serverName.Length > NetConfig.ServerNameMaxLength) { ServerName = ServerName.Substring(0, NetConfig.ServerNameMaxLength); }
|
||||
}
|
||||
}
|
||||
|
||||
private string serverMessageText;
|
||||
public string ServerMessageText
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace Barotrauma
|
||||
string[] readTags = valStr.Split(',');
|
||||
int matches = 0;
|
||||
foreach (string tag in readTags)
|
||||
if (((Item)target).HasTag(tag)) matches++;
|
||||
if (target is Item item && item.HasTag(tag)) matches++;
|
||||
|
||||
//If operator is == then it needs to match everything, otherwise if its != there must be zero matches.
|
||||
return Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0;
|
||||
@@ -225,8 +225,7 @@ namespace Barotrauma
|
||||
return success;
|
||||
case ConditionType.SpeciesName:
|
||||
if (target == null) { return Operator == OperatorType.NotEquals; }
|
||||
Character targetCharacter = target as Character;
|
||||
if (targetCharacter == null) { return false; }
|
||||
if (!(target is Character targetCharacter)) { return false; }
|
||||
return (Operator == OperatorType.Equals) == (targetCharacter.SpeciesName == valStr);
|
||||
case ConditionType.EntityType:
|
||||
switch (valStr)
|
||||
|
||||
@@ -12,8 +12,10 @@ namespace Barotrauma
|
||||
//only used if none of the selected content packages contain any text files
|
||||
const string VanillaTextFilePath = "Content/Texts/EnglishVanilla.xml";
|
||||
|
||||
private static readonly object mutex = new object();
|
||||
|
||||
//key = language
|
||||
private static Dictionary<string, List<TextPack>> textPacks = new Dictionary<string, List<TextPack>>();
|
||||
private static Dictionary<string, List<TextPack>> textPacks;
|
||||
|
||||
private static readonly string[] serverMessageCharacters = new string[] { "~", "[", "]", "=" };
|
||||
|
||||
@@ -25,10 +27,16 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> availableLanguages = new HashSet<string>();
|
||||
private static HashSet<string> availableLanguages;
|
||||
public static IEnumerable<string> AvailableLanguages
|
||||
{
|
||||
get { return availableLanguages; }
|
||||
get
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
return new HashSet<string>(availableLanguages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<string> GetTextFiles()
|
||||
@@ -55,25 +63,29 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public static string GetTranslatedLanguageName(string language)
|
||||
{
|
||||
if (!textPacks.ContainsKey(language))
|
||||
lock (mutex)
|
||||
{
|
||||
if (!textPacks.ContainsKey(language))
|
||||
{
|
||||
return language;
|
||||
}
|
||||
|
||||
foreach (var textPack in textPacks[language])
|
||||
{
|
||||
if (textPack.Language == language)
|
||||
{
|
||||
return textPack.TranslatedName;
|
||||
}
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
foreach (var textPack in textPacks[language])
|
||||
{
|
||||
if (textPack.Language == language)
|
||||
{
|
||||
return textPack.TranslatedName;
|
||||
}
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
public static void LoadTextPacks(IEnumerable<ContentPackage> selectedContentPackages)
|
||||
{
|
||||
availableLanguages.Clear();
|
||||
textPacks.Clear();
|
||||
HashSet<string> newLanguages = new HashSet<string>();
|
||||
Dictionary<string, List<TextPack>> newTextPacks = new Dictionary<string, List<TextPack>>();
|
||||
|
||||
var textFiles = ContentPackage.GetFilesOfType(selectedContentPackages, ContentType.Text);
|
||||
|
||||
foreach (string file in textFiles)
|
||||
@@ -81,12 +93,12 @@ namespace Barotrauma
|
||||
try
|
||||
{
|
||||
var textPack = new TextPack(file);
|
||||
availableLanguages.Add(textPack.Language);
|
||||
if (!textPacks.ContainsKey(textPack.Language))
|
||||
newLanguages.Add(textPack.Language);
|
||||
if (!newTextPacks.ContainsKey(textPack.Language))
|
||||
{
|
||||
textPacks.Add(textPack.Language, new List<TextPack>());
|
||||
newTextPacks.Add(textPack.Language, new List<TextPack>());
|
||||
}
|
||||
textPacks[textPack.Language].Add(textPack);
|
||||
newTextPacks[textPack.Language].Add(textPack);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -94,7 +106,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (textPacks.Count == 0)
|
||||
if (newTextPacks.Count == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("No text files available in any of the selected content packages. Attempting to find a vanilla English text file...");
|
||||
if (!File.Exists(VanillaTextFilePath))
|
||||
@@ -102,9 +114,21 @@ namespace Barotrauma
|
||||
throw new Exception("No text files found in any of the selected content packages or in the default text path!");
|
||||
}
|
||||
var textPack = new TextPack(VanillaTextFilePath);
|
||||
availableLanguages.Add(textPack.Language);
|
||||
textPacks.Add(textPack.Language, new List<TextPack>() { textPack });
|
||||
newLanguages.Add(textPack.Language);
|
||||
newTextPacks.Add(textPack.Language, new List<TextPack>() { textPack });
|
||||
}
|
||||
|
||||
if (newTextPacks.Count == 0)
|
||||
{
|
||||
throw new Exception("Failed to load text packs!");
|
||||
}
|
||||
|
||||
lock (mutex)
|
||||
{
|
||||
textPacks = newTextPacks;
|
||||
availableLanguages = newLanguages;
|
||||
}
|
||||
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
@@ -112,74 +136,81 @@ namespace Barotrauma
|
||||
{
|
||||
if (string.IsNullOrEmpty(textTag)) { return false; }
|
||||
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
lock (mutex)
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
}
|
||||
}
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
if (textPack.Get(textTag) != null) { return true; }
|
||||
}
|
||||
}
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
if (textPack.Get(textTag) != null) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string Get(string textTag, bool returnNull = false, string fallBackTag = null)
|
||||
{
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
lock (mutex)
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
string text = textPack.Get(textTag);
|
||||
if (text != null) { return text; }
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(fallBackTag))
|
||||
{
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
string text = textPack.Get(fallBackTag);
|
||||
if (text != null) { return text; }
|
||||
}
|
||||
}
|
||||
|
||||
//if text was not found and we're using a language other than English, see if we can find an English version
|
||||
//may happen, for example, if a user has selected another language and using mods that haven't been translated to that language
|
||||
if (Language != "English" && textPacks.ContainsKey("English"))
|
||||
{
|
||||
foreach (TextPack textPack in textPacks["English"])
|
||||
{
|
||||
string text = textPack.Get(textTag);
|
||||
if (text != null)
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Text \"" + textTag + "\" not found for the language \"" + Language + "\". Using the English text \"" + text + "\" instead.");
|
||||
#endif
|
||||
return text;
|
||||
throw new Exception("No text packs available in English!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (returnNull)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Text \"" + textTag + "\" not found.");
|
||||
return textTag;
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
string text = textPack.Get(textTag);
|
||||
if (text != null) { return text; }
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(fallBackTag))
|
||||
{
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
string text = textPack.Get(fallBackTag);
|
||||
if (text != null) { return text; }
|
||||
}
|
||||
}
|
||||
|
||||
//if text was not found and we're using a language other than English, see if we can find an English version
|
||||
//may happen, for example, if a user has selected another language and using mods that haven't been translated to that language
|
||||
if (Language != "English" && textPacks.ContainsKey("English"))
|
||||
{
|
||||
foreach (TextPack textPack in textPacks["English"])
|
||||
{
|
||||
string text = textPack.Get(textTag);
|
||||
if (text != null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Text \"" + textTag + "\" not found for the language \"" + Language + "\". Using the English text \"" + text + "\" instead.");
|
||||
#endif
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (returnNull)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Text \"" + textTag + "\" not found.");
|
||||
return textTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,13 +449,16 @@ namespace Barotrauma
|
||||
// And: replacement=formatter(value)
|
||||
public static string GetServerMessage(string serverMessage)
|
||||
{
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
lock (mutex)
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,58 +622,64 @@ namespace Barotrauma
|
||||
|
||||
public static List<string> GetAll(string textTag)
|
||||
{
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
lock (mutex)
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<string> allText;
|
||||
List<string> allText;
|
||||
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
allText = textPack.GetAll(textTag);
|
||||
if (allText != null) return allText;
|
||||
}
|
||||
|
||||
//if text was not found and we're using a language other than English, see if we can find an English version
|
||||
//may happen, for example, if a user has selected another language and using mods that haven't been translated to that language
|
||||
if (Language != "English" && textPacks.ContainsKey("English"))
|
||||
{
|
||||
foreach (TextPack textPack in textPacks["English"])
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
allText = textPack.GetAll(textTag);
|
||||
if (allText != null) return allText;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
//if text was not found and we're using a language other than English, see if we can find an English version
|
||||
//may happen, for example, if a user has selected another language and using mods that haven't been translated to that language
|
||||
if (Language != "English" && textPacks.ContainsKey("English"))
|
||||
{
|
||||
foreach (TextPack textPack in textPacks["English"])
|
||||
{
|
||||
allText = textPack.GetAll(textTag);
|
||||
if (allText != null) return allText;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<KeyValuePair<string, string>> GetAllTagTextPairs()
|
||||
{
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
lock (mutex)
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
}
|
||||
}
|
||||
|
||||
List<KeyValuePair<string, string>> allText = new List<KeyValuePair<string, string>>();
|
||||
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
allText.AddRange(textPack.GetAllTagTextPairs());
|
||||
}
|
||||
|
||||
return allText;
|
||||
}
|
||||
|
||||
List<KeyValuePair<string, string>> allText = new List<KeyValuePair<string, string>>();
|
||||
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
allText.AddRange(textPack.GetAllTagTextPairs());
|
||||
}
|
||||
|
||||
return allText;
|
||||
}
|
||||
|
||||
public static string ReplaceGenderPronouns(string text, Gender gender)
|
||||
@@ -689,35 +729,41 @@ namespace Barotrauma
|
||||
#if DEBUG
|
||||
public static void CheckForDuplicates(string lang)
|
||||
{
|
||||
if (!textPacks.ContainsKey(lang))
|
||||
lock (mutex)
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + lang + ")!");
|
||||
return;
|
||||
}
|
||||
if (!textPacks.ContainsKey(lang))
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + lang + ")!");
|
||||
return;
|
||||
}
|
||||
|
||||
int packIndex = 0;
|
||||
foreach (TextPack textPack in textPacks[lang])
|
||||
{
|
||||
textPack.CheckForDuplicates(packIndex);
|
||||
packIndex++;
|
||||
int packIndex = 0;
|
||||
foreach (TextPack textPack in textPacks[lang])
|
||||
{
|
||||
textPack.CheckForDuplicates(packIndex);
|
||||
packIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteToCSV()
|
||||
{
|
||||
string lang = "English";
|
||||
|
||||
if (!textPacks.ContainsKey(lang))
|
||||
lock (mutex)
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + lang + ")!");
|
||||
return;
|
||||
}
|
||||
string lang = "English";
|
||||
|
||||
int packIndex = 0;
|
||||
foreach (TextPack textPack in textPacks[lang])
|
||||
{
|
||||
textPack.WriteToCSV(packIndex);
|
||||
packIndex++;
|
||||
if (!textPacks.ContainsKey(lang))
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + lang + ")!");
|
||||
return;
|
||||
}
|
||||
|
||||
int packIndex = 0;
|
||||
foreach (TextPack textPack in textPacks[lang])
|
||||
{
|
||||
textPack.WriteToCSV(packIndex);
|
||||
packIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
72
Barotrauma/BarotraumaShared/Source/Utils/TaskPool.cs
Normal file
72
Barotrauma/BarotraumaShared/Source/Utils/TaskPool.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public static class TaskPool
|
||||
{
|
||||
private struct TaskAction
|
||||
{
|
||||
public Task Task;
|
||||
public Action<Task, object> OnCompletion;
|
||||
public object UserData;
|
||||
}
|
||||
|
||||
private static List<TaskAction> taskActions = new List<TaskAction>();
|
||||
|
||||
private static void AddInternal(Task task, Action<Task, object> onCompletion, object userdata)
|
||||
{
|
||||
lock (taskActions)
|
||||
{
|
||||
taskActions.Add(new TaskAction() { Task = task, OnCompletion = onCompletion, UserData = userdata });
|
||||
}
|
||||
}
|
||||
|
||||
public static void Add(Task task, Action<Task> onCompletion)
|
||||
{
|
||||
AddInternal(task, (Task t, object obj) => { onCompletion(t); }, null);
|
||||
}
|
||||
|
||||
public static void Add<U>(Task task, U userdata, Action<Task, U> onCompletion) where U : class
|
||||
{
|
||||
AddInternal(task, (Task t, object obj) => { onCompletion(t, (U)obj); }, userdata);
|
||||
}
|
||||
|
||||
public static void Add<T>(Task<T> task, Action<Task<T>> onCompletion)
|
||||
{
|
||||
AddInternal(task, (Task t, object obj) => { onCompletion((Task<T>)t); }, null);
|
||||
}
|
||||
|
||||
public static void Add<T,U>(Task<T> task, U userdata, Action<Task<T>, U> onCompletion) where U : class
|
||||
{
|
||||
AddInternal(task, (Task t, object obj) => { onCompletion((Task<T>)t, (U)obj); }, userdata);
|
||||
}
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
lock (taskActions)
|
||||
{
|
||||
for (int i = 0; i < taskActions.Count; i++)
|
||||
{
|
||||
if (taskActions[i].Task.IsCompleted)
|
||||
{
|
||||
taskActions[i].OnCompletion?.Invoke(taskActions[i].Task, taskActions[i].UserData);
|
||||
taskActions.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void PrintTaskExceptions(Task task, string msg)
|
||||
{
|
||||
DebugConsole.ThrowError(msg);
|
||||
foreach (Exception e in task.Exception.InnerExceptions)
|
||||
{
|
||||
DebugConsole.ThrowError(e.Message + "\n" + e.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -437,5 +437,90 @@ namespace Barotrauma
|
||||
IEnumerable<string> filtered = splitted.SkipWhile(part => part != currentFolder).Skip(1);
|
||||
return string.Join("/", filtered);
|
||||
}
|
||||
|
||||
public static string EscapeCharacters(string str)
|
||||
{
|
||||
return str.Replace("\\", "\\\\").Replace("\"", "\\\"");
|
||||
}
|
||||
|
||||
public static string UnescapeCharacters(string str)
|
||||
{
|
||||
string retVal = "";
|
||||
for (int i=0;i<str.Length;i++)
|
||||
{
|
||||
if (str[i] != '\\')
|
||||
{
|
||||
retVal += str[i];
|
||||
}
|
||||
else if (i+1<str.Length)
|
||||
{
|
||||
if (str[i+1] == '\\')
|
||||
{
|
||||
retVal += "\\";
|
||||
}
|
||||
else if (str[i+1] == '\"')
|
||||
{
|
||||
retVal += "\"";
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static string ParseQuotedArgument(string[] arguments, int startIndex, out int endIndex)
|
||||
{
|
||||
#if WINDOWS
|
||||
endIndex = startIndex + 1;
|
||||
return arguments[startIndex];
|
||||
#else
|
||||
string retVal = "";
|
||||
int currIndex = startIndex;
|
||||
bool escaped = false;
|
||||
if (arguments[startIndex][0] != '\"')
|
||||
{
|
||||
endIndex = startIndex+1;
|
||||
return UnescapeCharacters(arguments[startIndex]);
|
||||
}
|
||||
while (currIndex < arguments.Length)
|
||||
{
|
||||
for (int i=currIndex == startIndex ? 1 : 0;i<arguments[currIndex].Length;i++)
|
||||
{
|
||||
if (!escaped)
|
||||
{
|
||||
if (arguments[currIndex][i] == '\\')
|
||||
{
|
||||
escaped = true;
|
||||
}
|
||||
else if (arguments[currIndex][i] == '\"')
|
||||
{
|
||||
endIndex = currIndex+1;
|
||||
return UnescapeCharacters(retVal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
escaped = false;
|
||||
}
|
||||
retVal += arguments[currIndex][i];
|
||||
}
|
||||
retVal += " ";
|
||||
currIndex++;
|
||||
}
|
||||
|
||||
endIndex = arguments.Length;
|
||||
return retVal;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string[] MergeArguments(string[] arguments)
|
||||
{
|
||||
List<string> mergedArgs = new List<string>();
|
||||
for (int i=0;i<arguments.Length;)
|
||||
{
|
||||
mergedArgs.Add(ParseQuotedArgument(arguments, i, out i));
|
||||
}
|
||||
return mergedArgs.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,61 @@
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.9.6.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Networking/multiplayer fixes:
|
||||
- Fixed the game occasionally freezing when joining a server.
|
||||
- Fixed a bug that occasionally caused clients to disconnect with an "entity not found" error in the multiplayer. In technical terms, the issue occurred when the server spawned an entity with a given ID, sent a network event for that entity, and then spawned another entity that took up the ID of the previous entity. This could happen, for example, if in the multiplayer campaign a player happens to get an item with the same ID as an item in the inventory of another player who isn't currently present, and the other player joins the server. There are still most likely additional bugs that can cause similar error messages, so this fix will probably not get rid of the errors entirely.
|
||||
- Fixed start button staying disabled in the server lobby when switching from the campaign mode to another game mode.
|
||||
- Fixed clients occasionally getting disconnected due to the campaign store. Happened if a location had changed from one type to another (for example, natural formation to outpost), the players had bought something that wasn't originally available from the location and a new client joined.
|
||||
- Fixed clients occasionally being prompted to download subs from the server even if they already have a matching sub.
|
||||
- Fixed server having a password even if the password box is left empty in the in-game "host server" menu when hosting with the Linux version.
|
||||
- Fixed server name being shortened to one word when hosting from the in-game "host server" menu with the Linux version.
|
||||
- Fixed server name length not being restricted when set from serversettings.xml or using console commands.
|
||||
- Fixed "ready to start" tickbox disappearing if a client joins while a round is running.
|
||||
- Fixed traitors remaining as traitors client-side after restarting the round, allowing them to access the sabotage interface despite not having a traitor objective.
|
||||
- Fixed characters occasionally not selecting/deselecting a periscope client-side, causing them to walk in place or not grab the periscope.
|
||||
- Fixed male asian heads not appearing in the server lobby.
|
||||
- Fixed campaign servers never being filtered out from the server browser when filtering based on game mode.
|
||||
- Fixed clients not displaying the "could not connect" popup if the initial connection to a server times out.
|
||||
- Fixed door weld state occasionally getting desynced, causing the door to be welded server-side but possible to open client-side.
|
||||
|
||||
Electricity fixes:
|
||||
- Fixed junction boxes not passing signals to relays.
|
||||
- Fixed broken junction boxes and relays carrying power.
|
||||
- Fixed devices not receiving power if they're connected directly to batteries/supercapacitors with no relays or junction boxes in between.
|
||||
- Fixed light components that receive power from somewhere else than a power input connection not going out when they lose power (e.g. lights on the diving suits).
|
||||
- Fixed pumps and batteries operating/recharging faster when receiving overvoltage, with no upper limit, making it possible to for example operate pumps at 10x the speed by connecting them to a relay that's receiving 10x more power than is being drawn from it.
|
||||
- Fixed LightComponents being toggled twice when they receive a signal to the toggle connection.
|
||||
|
||||
Misc fixes:
|
||||
- Fixed "last used" listbox overlapping with the entity visibility tickboxes in the submarine editor on low resolutions.
|
||||
- Fixed misaligned colliders on the "Shell A Cap 0 deg A/B" wall pieces.
|
||||
- Fixed links disappearing between linked subs and docking ports when loading in the sub editor.
|
||||
- Fixed loading freezing for up to 10 seconds if the game cannot fetch the remote content for the main menu (update notifications, changelogs, etc).
|
||||
- Fixed lights on devices (junction boxes, nav terminals, reactor) being rendered on top of characters.
|
||||
- Fixed lights lagging a little behind moving objects (e.g. diving suit light).
|
||||
- Fixed characters being able to grab/heal others when handcuffed.
|
||||
- Character editor: don't switch to the limb mode when clicking a limb in the joint edit mode. Fixes difficulties in using the joint angle widgets when the widgets are interloping limb source rects.
|
||||
- Character editor: Fixed the texture path field not being copied if the source character has no texture path defined. In this case, we can use a texture path found in one of the limbs.
|
||||
- Fixed bots incorrectly reporting problems in the room they are in instead of the room the issues is spotted at.
|
||||
- Fixed bots reporting broken devices or asking medical attendance even if they are taking care of those things.
|
||||
- Fixed bots saying "put out a fire in hull" instead of using the name of the room when they extinguish a fire.
|
||||
- Fixed AITargets staing active even if the entity is not active (for example, the sonar could still be heard by monsters after it's turned off).
|
||||
- The bots now only report about issues that have not been already been reported.
|
||||
- Fixed monsters attacking doors that are open.
|
||||
- Monsters keep pursuing the target if the behavior is "pursue" even when they cannot attack.
|
||||
- Bots stop operating devices if a player starts operating them (unless they have been ordered to operate the device).
|
||||
- Fixed bots playing the rewiring sound when repairing an item with a screwdriver.
|
||||
- Fixed bots sometimes stopping behind a closed door when trying to extinguish a fire.
|
||||
- Fixed chat messages occasionally extending below the lower bound of the chatbox.
|
||||
- Fixed searchlight and turret lights disappearing before the light is fully off-screen.
|
||||
- Waypoint fixes in Kastrull and the drone.
|
||||
- Fixed Kastrull flooding when the drone is undocked.
|
||||
- Fixed ballast pumps in Kastrull's drone being off by default.
|
||||
- Fixed enormous in-game ballistic helmet sprite.
|
||||
- Fire extinguishers can't be placed inside toolboxes anymore.
|
||||
- Fixed lobby screen not scaling correctly after changing to a bigger resolution.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.9.5.1
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
@@ -8,8 +66,7 @@ v0.9.5.1
|
||||
- Nerfed Hammerhead Spawns.
|
||||
- Fixed occasional disconnections when the Hammerhead Matriarch releases her spawns.
|
||||
- Fixed Hammerhead Matriarch exploding twice when it attacks.
|
||||
- Fixed bots sometimes getting stuck in an objective loop, causing them to repeatedly drop and pick up
|
||||
diving suits or spam doors.
|
||||
- Fixed bots sometimes getting stuck in an objective loop, causing them to repeatedly drop and pick up diving suits or spam doors.
|
||||
- Fixed bots being allowed to go outside without a diving suit.
|
||||
- Fixed characters with a rectangular main collider being unable to use path finding.
|
||||
- Fixed microphone volume scrollbar resetting to the maximum value when opening the settings menu.
|
||||
|
||||
Reference in New Issue
Block a user