Merge https://github.com/Regalis11/Barotrauma into develop
This commit is contained in:
@@ -54,7 +54,7 @@ body:
|
|||||||
label: Version
|
label: Version
|
||||||
description: Which version of the game did the bug happen in? You can see the current version number in the bottom left corner of your screen in the main menu.
|
description: Which version of the game did the bug happen in? You can see the current version number in the bottom left corner of your screen in the main menu.
|
||||||
options:
|
options:
|
||||||
- v1.0.13.2
|
- v1.0.20.1
|
||||||
- Other
|
- Other
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace Barotrauma
|
|||||||
var target = _selectedAiTarget ?? _lastAiTarget;
|
var target = _selectedAiTarget ?? _lastAiTarget;
|
||||||
if (target != null && target.Entity != null)
|
if (target != null && target.Entity != null)
|
||||||
{
|
{
|
||||||
var memory = GetTargetMemory(target, false);
|
var memory = GetTargetMemory(target);
|
||||||
if (memory != null)
|
if (memory != null)
|
||||||
{
|
{
|
||||||
Vector2 targetPos = memory.Location;
|
Vector2 targetPos = memory.Location;
|
||||||
|
|||||||
@@ -214,7 +214,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
|
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
|
||||||
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
|
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
|
||||||
TransformCursorPos();
|
|
||||||
|
|
||||||
bool ragdollInput = msg.ReadBoolean();
|
bool ragdollInput = msg.ReadBoolean();
|
||||||
keys[(int)InputType.Ragdoll].Held = ragdollInput;
|
keys[(int)InputType.Ragdoll].Held = ragdollInput;
|
||||||
|
|||||||
@@ -850,7 +850,7 @@ namespace Barotrauma
|
|||||||
if (treatmentButton.Enabled && treatmentButton.State == GUIComponent.ComponentState.Hover)
|
if (treatmentButton.Enabled && treatmentButton.State == GUIComponent.ComponentState.Hover)
|
||||||
{
|
{
|
||||||
//highlight the slot the treatment item is in
|
//highlight the slot the treatment item is in
|
||||||
var rootContainer = matchingItem.GetRootContainer() ?? matchingItem;
|
var rootContainer = matchingItem.RootContainer ?? matchingItem;
|
||||||
var index = Character.Controlled.Inventory.FindIndex(rootContainer);
|
var index = Character.Controlled.Inventory.FindIndex(rootContainer);
|
||||||
if (Character.Controlled.Inventory.visualSlots != null && index > -1 && index < Character.Controlled.Inventory.visualSlots.Length &&
|
if (Character.Controlled.Inventory.visualSlots != null && index > -1 && index < Character.Controlled.Inventory.visualSlots.Length &&
|
||||||
Character.Controlled.Inventory.visualSlots[index].HighlightTimer <= 0.0f)
|
Character.Controlled.Inventory.visualSlots[index].HighlightTimer <= 0.0f)
|
||||||
|
|||||||
@@ -870,7 +870,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (wearable.Type == WearableType.Hair)
|
if (wearable.Type == WearableType.Hair)
|
||||||
{
|
{
|
||||||
if (HairWithHatSprite != null)
|
if (HairWithHatSprite != null && !hideLimb)
|
||||||
{
|
{
|
||||||
DrawWearable(HairWithHatSprite, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
DrawWearable(HairWithHatSprite, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
||||||
depthStep += step;
|
depthStep += step;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Microsoft.Xna.Framework;
|
|||||||
using Microsoft.Xna.Framework.Input;
|
using Microsoft.Xna.Framework.Input;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -1156,6 +1157,26 @@ namespace Barotrauma
|
|||||||
});
|
});
|
||||||
AssignRelayToServer("debugdraw", false);
|
AssignRelayToServer("debugdraw", false);
|
||||||
|
|
||||||
|
AssignOnExecute("debugdrawlos", (string[] args) =>
|
||||||
|
{
|
||||||
|
if (args.None() || !bool.TryParse(args[0], out bool state))
|
||||||
|
{
|
||||||
|
state = !GameMain.LightManager.DebugLos;
|
||||||
|
}
|
||||||
|
GameMain.LightManager.DebugLos = state;
|
||||||
|
NewMessage("Los debug draw mode " + (GameMain.LightManager.DebugLos ? "enabled" : "disabled"), Color.Yellow);
|
||||||
|
});
|
||||||
|
AssignOnExecute("debugwiring", (string[] args) =>
|
||||||
|
{
|
||||||
|
if (args.None() || !bool.TryParse(args[0], out bool state))
|
||||||
|
{
|
||||||
|
state = !ConnectionPanel.DebugWiringMode;
|
||||||
|
}
|
||||||
|
ConnectionPanel.DebugWiringMode = state;
|
||||||
|
NewMessage("Wiring debug mode " + (ConnectionPanel.DebugWiringMode ? "enabled" : "disabled"), Color.Yellow);
|
||||||
|
});
|
||||||
|
AssignRelayToServer("debugdraw", false);
|
||||||
|
|
||||||
AssignOnExecute("devmode", (string[] args) =>
|
AssignOnExecute("devmode", (string[] args) =>
|
||||||
{
|
{
|
||||||
if (args.None() || !bool.TryParse(args[0], out bool state))
|
if (args.None() || !bool.TryParse(args[0], out bool state))
|
||||||
@@ -2824,7 +2845,26 @@ namespace Barotrauma
|
|||||||
ContentPackageManager.EnabledPackages.ReloadCore();
|
ContentPackageManager.EnabledPackages.ReloadCore();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
#warning TODO: reimplement?
|
#if WINDOWS
|
||||||
|
commands.Add(new Command("startdedicatedserver", "", (string[] args) =>
|
||||||
|
{
|
||||||
|
Process.Start("DedicatedServer.exe");
|
||||||
|
}));
|
||||||
|
|
||||||
|
commands.Add(new Command("editserversettings", "", (string[] args) =>
|
||||||
|
{
|
||||||
|
if (Process.GetProcessesByName("DedicatedServer").Length > 0)
|
||||||
|
{
|
||||||
|
NewMessage("Can't be edited if DedicatedServer.exe is already running", Color.Red);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Process.Start("notepad.exe", "serversettings.xml");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#warning TODO: reimplement?
|
||||||
/*commands.Add(new Command("ingamemodswap", "", (string[] args) =>
|
/*commands.Add(new Command("ingamemodswap", "", (string[] args) =>
|
||||||
{
|
{
|
||||||
ContentPackage.IngameModSwap = !ContentPackage.IngameModSwap;
|
ContentPackage.IngameModSwap = !ContentPackage.IngameModSwap;
|
||||||
|
|||||||
@@ -662,34 +662,36 @@ namespace Barotrauma
|
|||||||
Identifier missionIdentifier = msg.ReadIdentifier();
|
Identifier missionIdentifier = msg.ReadIdentifier();
|
||||||
int locationIndex = msg.ReadInt32();
|
int locationIndex = msg.ReadInt32();
|
||||||
int destinationIndex = msg.ReadInt32();
|
int destinationIndex = msg.ReadInt32();
|
||||||
|
|
||||||
string missionName = msg.ReadString();
|
string missionName = msg.ReadString();
|
||||||
MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier);
|
if (Screen.Selected != GameMain.NetLobbyScreen)
|
||||||
if (prefab != null)
|
|
||||||
{
|
{
|
||||||
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", missionName),
|
MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier);
|
||||||
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
|
if (prefab != null)
|
||||||
{
|
{
|
||||||
IconColor = prefab.IconColor
|
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", missionName),
|
||||||
};
|
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
|
||||||
if (GameMain.GameSession?.Map is { } map && locationIndex >= 0 && locationIndex < map.Locations.Count)
|
{
|
||||||
{
|
IconColor = prefab.IconColor
|
||||||
Location location = map.Locations[locationIndex];
|
};
|
||||||
map.Discover(location, checkTalents: false);
|
if (GameMain.GameSession?.Map is { } map && locationIndex >= 0 && locationIndex < map.Locations.Count)
|
||||||
|
{
|
||||||
|
Location location = map.Locations[locationIndex];
|
||||||
|
map.Discover(location, checkTalents: false);
|
||||||
|
|
||||||
LocationConnection? connection = null;
|
LocationConnection? connection = null;
|
||||||
if (destinationIndex != locationIndex && destinationIndex >= 0 && destinationIndex < map.Locations.Count)
|
if (destinationIndex != locationIndex && destinationIndex >= 0 && destinationIndex < map.Locations.Count)
|
||||||
{
|
{
|
||||||
Location destination = map.Locations[destinationIndex];
|
Location destination = map.Locations[destinationIndex];
|
||||||
connection = map.Connections.FirstOrDefault(c => c.Locations.Contains(location) && c.Locations.Contains(destination));
|
connection = map.Connections.FirstOrDefault(c => c.Locations.Contains(location) && c.Locations.Contains(destination));
|
||||||
}
|
}
|
||||||
if (connection != null)
|
if (connection != null)
|
||||||
{
|
{
|
||||||
location.UnlockMission(prefab, connection);
|
location.UnlockMission(prefab, connection);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
location.UnlockMission(prefab);
|
location.UnlockMission(prefab);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < resourceClusters.Count; i++)
|
for (int i = 0; i < resourceAmounts.Count; i++)
|
||||||
{
|
{
|
||||||
var amount = msg.ReadByte();
|
var amount = msg.ReadByte();
|
||||||
var rotation = msg.ReadSingle();
|
var rotation = msg.ReadSingle();
|
||||||
@@ -54,7 +54,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
CalculateMissionClusterPositions();
|
CalculateMissionClusterPositions();
|
||||||
|
|
||||||
for(int i = 0; i < resourceClusters.Count; i++)
|
for(int i = 0; i < resourceAmounts.Count; i++)
|
||||||
{
|
{
|
||||||
var identifier = msg.ReadIdentifier();
|
var identifier = msg.ReadIdentifier();
|
||||||
var count = msg.ReadByte();
|
var count = msg.ReadByte();
|
||||||
|
|||||||
@@ -88,13 +88,14 @@ namespace Barotrauma
|
|||||||
public static TextManager.SpeciallyHandledCharCategory ExtractShccFromXElement(XElement element)
|
public static TextManager.SpeciallyHandledCharCategory ExtractShccFromXElement(XElement element)
|
||||||
=> TextManager.SpeciallyHandledCharCategories
|
=> TextManager.SpeciallyHandledCharCategories
|
||||||
.Where(category => element.GetAttributeBool($"is{category}", category switch {
|
.Where(category => element.GetAttributeBool($"is{category}", category switch {
|
||||||
// CJK isn't supported by default
|
// CJK and Japanese aren't supported by default
|
||||||
TextManager.SpeciallyHandledCharCategory.CJK => false,
|
TextManager.SpeciallyHandledCharCategory.CJK => false,
|
||||||
|
TextManager.SpeciallyHandledCharCategory.Japanese => false,
|
||||||
|
|
||||||
// For backwards compatibility, we assume that Cyrillic is supported by default
|
// For backwards compatibility, we assume that Cyrillic is supported by default
|
||||||
TextManager.SpeciallyHandledCharCategory.Cyrillic => true,
|
TextManager.SpeciallyHandledCharCategory.Cyrillic => true,
|
||||||
|
|
||||||
_ => throw new Exception("unreachable")
|
_ => throw new NotImplementedException($"nameof{category} not implemented.")
|
||||||
}))
|
}))
|
||||||
.Aggregate(TextManager.SpeciallyHandledCharCategory.None, (current, category) => current | category);
|
.Aggregate(TextManager.SpeciallyHandledCharCategory.None, (current, category) => current | category);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Barotrauma.Extensions;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Barotrauma.Extensions;
|
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -199,8 +199,8 @@ namespace Barotrauma
|
|||||||
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
|
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
|
||||||
{
|
{
|
||||||
size = new Point(
|
size = new Point(
|
||||||
subElement.GetAttributeInt("width", 0),
|
ParseSize(subElement, "width"),
|
||||||
subElement.GetAttributeInt("height", 0));
|
ParseSize(subElement, "height"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -533,7 +533,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (characterInfo.MinReputationToHire.factionId != Identifier.Empty)
|
if (characterInfo.MinReputationToHire.factionId != Identifier.Empty)
|
||||||
{
|
{
|
||||||
if (campaign.GetReputation(characterInfo.MinReputationToHire.factionId) < characterInfo.MinReputationToHire.reputation)
|
if (MathF.Round(campaign.GetReputation(characterInfo.MinReputationToHire.factionId)) < characterInfo.MinReputationToHire.reputation)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Barotrauma.Extensions;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Barotrauma.Extensions;
|
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -20,6 +18,26 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
return element.NameAsIdentifier();
|
return element.NameAsIdentifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected int ParseSize(XElement element, string attributeName)
|
||||||
|
{
|
||||||
|
string valueStr = element.GetAttributeString(attributeName, string.Empty);
|
||||||
|
bool relativeToWidth = valueStr.EndsWith("vw");
|
||||||
|
bool relativeToHeight = valueStr.EndsWith("vh");
|
||||||
|
if (relativeToWidth || relativeToHeight)
|
||||||
|
{
|
||||||
|
string floatStr = valueStr.Substring(0, valueStr.Length - 2);
|
||||||
|
if (!float.TryParse(floatStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float relativeHeight))
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error while parsing a {nameof(GUIComponentStyle)}: {valueStr} is not a valid size.");
|
||||||
|
}
|
||||||
|
return (int)(relativeHeight / 100.0f * (relativeToWidth ? GameMain.GraphicsWidth : GameMain.GraphicsHeight));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return element.GetAttributeInt(attributeName, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class GUISelector<T> where T : GUIPrefab
|
public abstract class GUISelector<T> where T : GUIPrefab
|
||||||
@@ -166,7 +184,8 @@ namespace Barotrauma
|
|||||||
Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue));
|
Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue));
|
||||||
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
|
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
|
||||||
{
|
{
|
||||||
return (uint)Math.Round(subElement.GetAttributeInt("size", 14) * GameSettings.CurrentConfig.Graphics.TextScale);
|
int rawSize = ParseSize(subElement, "size");
|
||||||
|
return (uint)Math.Round(rawSize * GameSettings.CurrentConfig.Graphics.TextScale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (uint)Math.Round(defaultSize * GameSettings.CurrentConfig.Graphics.TextScale);
|
return (uint)Math.Round(defaultSize * GameSettings.CurrentConfig.Graphics.TextScale);
|
||||||
|
|||||||
@@ -542,6 +542,7 @@ namespace Barotrauma
|
|||||||
void drawRect(Vector2 topLeft, Vector2 bottomRight)
|
void drawRect(Vector2 topLeft, Vector2 bottomRight)
|
||||||
{
|
{
|
||||||
int minWidth = GUI.IntScale(5);
|
int minWidth = GUI.IntScale(5);
|
||||||
|
if (OverflowClip) { topLeft.X = Math.Max(topLeft.X, 0.0f); }
|
||||||
if (bottomRight.X - topLeft.X < minWidth) { bottomRight.X = topLeft.X + minWidth; }
|
if (bottomRight.X - topLeft.X < minWidth) { bottomRight.X = topLeft.X + minWidth; }
|
||||||
GUI.DrawRectangle(spriteBatch,
|
GUI.DrawRectangle(spriteBatch,
|
||||||
Rect.Location.ToVector2() + topLeft,
|
Rect.Location.ToVector2() + topLeft,
|
||||||
|
|||||||
@@ -159,8 +159,9 @@ namespace Barotrauma
|
|||||||
|
|
||||||
int crewAreaY = ButtonAreaTop.Bottom + Padding;
|
int crewAreaY = ButtonAreaTop.Bottom + Padding;
|
||||||
int crewAreaHeight = ObjectiveAnchor.Top - Padding - crewAreaY;
|
int crewAreaHeight = ObjectiveAnchor.Top - Padding - crewAreaY;
|
||||||
CrewArea = new Rectangle(Padding, crewAreaY, (int)Math.Max(400 * GUI.Scale, 220), crewAreaHeight);
|
|
||||||
|
|
||||||
|
float crewAreaWidthMultiplier = GUI.IsUltrawide ? GUI.HorizontalAspectRatio : 1.0f;
|
||||||
|
CrewArea = new Rectangle(Padding, crewAreaY, (int)(Math.Max(400 * GUI.Scale, 220) * crewAreaWidthMultiplier), crewAreaHeight);
|
||||||
InventoryAreaLower = new Rectangle(ChatBoxArea.Right + Padding * 7, inventoryTopY, GameMain.GraphicsWidth - Padding * 9 - ChatBoxArea.Width, GameMain.GraphicsHeight - inventoryTopY);
|
InventoryAreaLower = new Rectangle(ChatBoxArea.Right + Padding * 7, inventoryTopY, GameMain.GraphicsWidth - Padding * 9 - ChatBoxArea.Width, GameMain.GraphicsHeight - inventoryTopY);
|
||||||
|
|
||||||
int healthWindowWidth = (int)(GameMain.GraphicsWidth * 0.5f);
|
int healthWindowWidth = (int)(GameMain.GraphicsWidth * 0.5f);
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (relativeOffset.NearlyEquals(value)) { return; }
|
if (relativeOffset.NearlyEquals(value)) { return; }
|
||||||
relativeOffset = value;
|
relativeOffset = value;
|
||||||
|
recalculateRect = true;
|
||||||
RecalculateChildren(false, false);
|
RecalculateChildren(false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -870,7 +870,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (var minRep in priceInfo.MinReputation)
|
foreach (var minRep in priceInfo.MinReputation)
|
||||||
{
|
{
|
||||||
if (campaign.GetReputation(minRep.Key) < minRep.Value)
|
if (MathF.Round(campaign.GetReputation(minRep.Key)) < minRep.Value)
|
||||||
{
|
{
|
||||||
return minRep;
|
return minRep;
|
||||||
}
|
}
|
||||||
@@ -1930,7 +1930,7 @@ namespace Barotrauma
|
|||||||
"campaignstore.reputationrequired",
|
"campaignstore.reputationrequired",
|
||||||
("[amount]", ((int)requiredReputation.Value.Value).ToString()),
|
("[amount]", ((int)requiredReputation.Value.Value).ToString()),
|
||||||
("[faction]", TextManager.Get("faction." + requiredReputation.Value.Key).Value));
|
("[faction]", TextManager.Get("faction." + requiredReputation.Value.Key).Value));
|
||||||
Color color = campaign.GetReputation(requiredReputation.Value.Key) < requiredReputation.Value.Value ?
|
Color color = MathF.Round(campaign.GetReputation(requiredReputation.Value.Key)) < requiredReputation.Value.Value ?
|
||||||
GUIStyle.Orange : GUIStyle.Green;
|
GUIStyle.Orange : GUIStyle.Green;
|
||||||
toolTip += $"\n‖color:{color.ToStringHex()}‖{repStr}‖color:end‖";
|
toolTip += $"\n‖color:{color.ToStringHex()}‖{repStr}‖color:end‖";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -807,8 +807,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (GameMain.Client == null)
|
if (GameMain.Client == null)
|
||||||
{
|
{
|
||||||
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
|
if (GameMain.GameSession.TryPurchaseSubmarine(selectedSubmarine))
|
||||||
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch);
|
{
|
||||||
|
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch);
|
||||||
|
}
|
||||||
RefreshSubmarineDisplay(true);
|
RefreshSubmarineDisplay(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -829,7 +831,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (GameMain.Client == null)
|
if (GameMain.Client == null)
|
||||||
{
|
{
|
||||||
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
|
GameMain.GameSession.TryPurchaseSubmarine(selectedSubmarine);
|
||||||
RefreshSubmarineDisplay(true);
|
RefreshSubmarineDisplay(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1722,7 +1722,7 @@ namespace Barotrauma
|
|||||||
static void CreateMaterialCosts(GUIListBox list, UpgradePrefab prefab, int targetLevel)
|
static void CreateMaterialCosts(GUIListBox list, UpgradePrefab prefab, int targetLevel)
|
||||||
{
|
{
|
||||||
list.Content.ClearChildren();
|
list.Content.ClearChildren();
|
||||||
List<Item> allItems = Character.Controlled?.Inventory?.FindAllItems(recursive: true) ?? new List<Item>();
|
var allItems = CargoManager.FindAllItemsOnPlayerAndSub(Character.Controlled);
|
||||||
|
|
||||||
var resources = prefab.GetApplicableResources(targetLevel);
|
var resources = prefab.GetApplicableResources(targetLevel);
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,12 @@ namespace Barotrauma
|
|||||||
private float votingTime = 100f;
|
private float votingTime = 100f;
|
||||||
private float timer;
|
private float timer;
|
||||||
private VoteType currentVoteType;
|
private VoteType currentVoteType;
|
||||||
private Color SubmarineColor => GUIStyle.Orange;
|
private static Color SubmarineColor => GUIStyle.Orange;
|
||||||
private Point createdForResolution;
|
private Point createdForResolution;
|
||||||
|
|
||||||
|
//timer ran out but server still hasn't notified of the result of the vote
|
||||||
|
public bool TimedOut => VoteRunning && timer - votingTime > 10.0f;
|
||||||
|
|
||||||
public static VotingInterface CreateSubmarineVotingInterface(Client starter, SubmarineInfo info, VoteType type, bool transferItems, float votingTime)
|
public static VotingInterface CreateSubmarineVotingInterface(Client starter, SubmarineInfo info, VoteType type, bool transferItems, float votingTime)
|
||||||
{
|
{
|
||||||
if (starter == null || info == null) { return null; }
|
if (starter == null || info == null) { return null; }
|
||||||
|
|||||||
@@ -672,7 +672,10 @@ namespace Barotrauma
|
|||||||
while (Timing.Accumulator >= Timing.Step)
|
while (Timing.Accumulator >= Timing.Step)
|
||||||
{
|
{
|
||||||
Timing.TotalTime += Timing.Step;
|
Timing.TotalTime += Timing.Step;
|
||||||
|
if (!Paused)
|
||||||
|
{
|
||||||
|
Timing.TotalTimeUnpaused += Timing.Step;
|
||||||
|
}
|
||||||
Stopwatch sw = new Stopwatch();
|
Stopwatch sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
|
|
||||||
@@ -956,7 +959,10 @@ namespace Barotrauma
|
|||||||
PerformanceCounter.UpdateTimeGraph.Update(sw.ElapsedTicks * 1000.0f / (float)Stopwatch.Frequency);
|
PerformanceCounter.UpdateTimeGraph.Update(sw.ElapsedTicks * 1000.0f / (float)Stopwatch.Frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Paused) { Timing.Alpha = Timing.Accumulator / Timing.Step; }
|
if (!Paused)
|
||||||
|
{
|
||||||
|
Timing.Alpha = Timing.Accumulator / Timing.Step;
|
||||||
|
}
|
||||||
|
|
||||||
if (performanceCounterTimer.ElapsedMilliseconds > 1000)
|
if (performanceCounterTimer.ElapsedMilliseconds > 1000)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace Barotrauma
|
|||||||
// Item must be in a non-equipment slot if possible
|
// Item must be in a non-equipment slot if possible
|
||||||
if (!item.AllowedSlots.All(s => equipmentSlots.Contains(s)) && IsInEquipmentSlot(item)) { return false; }
|
if (!item.AllowedSlots.All(s => equipmentSlots.Contains(s)) && IsInEquipmentSlot(item)) { return false; }
|
||||||
// Item must not be contained inside an item in an equipment slot
|
// Item must not be contained inside an item in an equipment slot
|
||||||
if (item.GetRootContainer() is Item rootContainer && IsInEquipmentSlot(rootContainer)) { return false; }
|
if (item.RootContainer is Item rootContainer && IsInEquipmentSlot(rootContainer)) { return false; }
|
||||||
return true;
|
return true;
|
||||||
}, recursive: true).Distinct();
|
}, recursive: true).Distinct();
|
||||||
|
|
||||||
|
|||||||
@@ -376,6 +376,7 @@ namespace Barotrauma
|
|||||||
- (0.1f * iconRelativeWidth)
|
- (0.1f * iconRelativeWidth)
|
||||||
// Spacing
|
// Spacing
|
||||||
- (7 * layoutGroup.RelativeSpacing);
|
- (7 * layoutGroup.RelativeSpacing);
|
||||||
|
nameRelativeWidth = Math.Max(nameRelativeWidth, 0.25f);
|
||||||
|
|
||||||
var font = layoutGroup.Rect.Width < 150 ? GUIStyle.SmallFont : GUIStyle.Font;
|
var font = layoutGroup.Rect.Width < 150 ? GUIStyle.SmallFont : GUIStyle.Font;
|
||||||
var nameBlock = new GUITextBlock(
|
var nameBlock = new GUITextBlock(
|
||||||
|
|||||||
@@ -121,6 +121,23 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
return AllowedToManageCampaign(ClientPermissions.ManageMoney);
|
return AllowedToManageCampaign(ClientPermissions.ManageMoney);
|
||||||
}
|
}
|
||||||
|
protected GUIButton CreateEndRoundButton()
|
||||||
|
{
|
||||||
|
int buttonWidth = (int)(450 * GUI.xScale * (GUI.IsUltrawide ? 3.0f : 1.0f));
|
||||||
|
int buttonHeight = (int)(40 * GUI.yScale);
|
||||||
|
var rectT = HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y, buttonWidth, buttonHeight), GUI.Canvas);
|
||||||
|
rectT.Pivot = Pivot.Center;
|
||||||
|
return new GUIButton(rectT, TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
|
||||||
|
{
|
||||||
|
Pulse = true,
|
||||||
|
TextBlock =
|
||||||
|
{
|
||||||
|
Shadow = true,
|
||||||
|
AutoScaleHorizontal = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void Draw(SpriteBatch spriteBatch)
|
public override void Draw(SpriteBatch spriteBatch)
|
||||||
{
|
{
|
||||||
|
|||||||
+17
-30
@@ -125,38 +125,25 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void CreateButtons()
|
private void CreateButtons()
|
||||||
{
|
{
|
||||||
int buttonHeight = (int) (GUI.Scale * 40),
|
endRoundButton = CreateEndRoundButton();
|
||||||
buttonWidth = GUI.IntScale(450),
|
endRoundButton.OnClicked = (btn, userdata) =>
|
||||||
buttonCenter = buttonHeight / 2,
|
|
||||||
screenMiddle = GameMain.GraphicsWidth / 2;
|
|
||||||
|
|
||||||
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle - buttonWidth / 2, HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, buttonWidth, buttonHeight), GUI.Canvas),
|
|
||||||
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
|
|
||||||
{
|
{
|
||||||
Pulse = true,
|
TryEndRoundWithFuelCheck(
|
||||||
TextBlock =
|
onConfirm: () => GameMain.Client.RequestStartRound(),
|
||||||
{
|
onReturnToMapScreen: () =>
|
||||||
Shadow = true,
|
{
|
||||||
AutoScaleHorizontal = true
|
ShowCampaignUI = true;
|
||||||
},
|
if (CampaignUI == null) { InitCampaignUI(); }
|
||||||
OnClicked = (btn, userdata) =>
|
CampaignUI.SelectTab(InteractionType.Map);
|
||||||
{
|
});
|
||||||
TryEndRoundWithFuelCheck(
|
return true;
|
||||||
onConfirm: () => GameMain.Client.RequestStartRound(),
|
|
||||||
onReturnToMapScreen: () =>
|
|
||||||
{
|
|
||||||
ShowCampaignUI = true;
|
|
||||||
if (CampaignUI == null) { InitCampaignUI(); }
|
|
||||||
CampaignUI.SelectTab(InteractionType.Map);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int readyButtonHeight = buttonHeight;
|
int readyButtonWidth = (int)(GUI.Scale * 50 * (GUI.IsUltrawide ? 3.0f : 1.0f));
|
||||||
int readyButtonWidth = (int) (GUI.Scale * 50);
|
int readyButtonHeight = (int)(GUI.Scale * 40);
|
||||||
|
int readyButtonCenter = readyButtonHeight / 2,
|
||||||
ReadyCheckButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle + (buttonWidth / 2) + GUI.IntScale(16), HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, readyButtonWidth, readyButtonHeight), GUI.Canvas),
|
screenMiddle = GameMain.GraphicsWidth / 2;
|
||||||
|
ReadyCheckButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle + (endRoundButton.Rect.Width / 2) + GUI.IntScale(16), HUDLayoutSettings.ButtonAreaTop.Center.Y - readyButtonCenter, readyButtonWidth, readyButtonHeight), GUI.Canvas),
|
||||||
style: "RepairBuyButton")
|
style: "RepairBuyButton")
|
||||||
{
|
{
|
||||||
ToolTip = TextManager.Get("ReadyCheck.Tooltip"),
|
ToolTip = TextManager.Get("ReadyCheck.Tooltip"),
|
||||||
@@ -206,7 +193,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (GameMain.Client == null)
|
if (GameMain.Client == null)
|
||||||
{
|
{
|
||||||
yield return CoroutineStatus.Failure;
|
yield return CoroutineStatus.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GameMain.Client.LateCampaignJoin)
|
if (GameMain.Client.LateCampaignJoin)
|
||||||
|
|||||||
+14
-20
@@ -148,6 +148,9 @@ namespace Barotrauma
|
|||||||
case "stats":
|
case "stats":
|
||||||
LoadStats(subElement);
|
LoadStats(subElement);
|
||||||
break;
|
break;
|
||||||
|
case "eventmanager":
|
||||||
|
GameMain.GameSession.EventManager.Load(subElement);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,28 +213,14 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
StartRound = () => { TryEndRound(); }
|
StartRound = () => { TryEndRound(); }
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateEndRoundButton()
|
endRoundButton = CreateEndRoundButton();
|
||||||
{
|
endRoundButton.OnClicked = (btn, userdata) =>
|
||||||
int buttonHeight = (int)(GUI.Scale * 40);
|
|
||||||
int buttonWidth = GUI.IntScale(450);
|
|
||||||
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUI.Canvas),
|
|
||||||
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
|
|
||||||
{
|
{
|
||||||
Pulse = true,
|
TryEndRoundWithFuelCheck(
|
||||||
TextBlock =
|
onConfirm: () => TryEndRound(),
|
||||||
{
|
onReturnToMapScreen: () => { ShowCampaignUI = true; CampaignUI.SelectTab(InteractionType.Map); });
|
||||||
Shadow = true,
|
return true;
|
||||||
AutoScaleHorizontal = true
|
|
||||||
},
|
|
||||||
OnClicked = (btn, userdata) =>
|
|
||||||
{
|
|
||||||
TryEndRoundWithFuelCheck(
|
|
||||||
onConfirm: () => TryEndRound(),
|
|
||||||
onReturnToMapScreen: () => { ShowCampaignUI = true; CampaignUI.SelectTab(InteractionType.Map); });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,6 +688,11 @@ namespace Barotrauma
|
|||||||
modeElement.Add(Settings.Save());
|
modeElement.Add(Settings.Save());
|
||||||
modeElement.Add(SaveStats());
|
modeElement.Add(SaveStats());
|
||||||
|
|
||||||
|
if (GameMain.GameSession?.EventManager != null)
|
||||||
|
{
|
||||||
|
modeElement.Add(GameMain.GameSession?.EventManager.Save());
|
||||||
|
}
|
||||||
|
|
||||||
//save and remove all items that are in someone's inventory so they don't get included in the sub file as well
|
//save and remove all items that are in someone's inventory so they don't get included in the sub file as well
|
||||||
foreach (Character c in Character.CharacterList)
|
foreach (Character c in Character.CharacterList)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -310,9 +310,10 @@ namespace Barotrauma
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||||
RichString.Rich(missionMessage), wrap: true);
|
RichString.Rich(missionMessage), wrap: true);
|
||||||
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed)
|
if (selectedMissions.Contains(displayedMission))
|
||||||
{
|
{
|
||||||
RichString reputationText = displayedMission.GetReputationRewardText();
|
RichString reputationText = displayedMission.GetReputationRewardText();
|
||||||
if (!reputationText.IsNullOrEmpty())
|
if (!reputationText.IsNullOrEmpty())
|
||||||
@@ -324,7 +325,7 @@ namespace Barotrauma
|
|||||||
if (totalReward > 0)
|
if (totalReward > 0)
|
||||||
{
|
{
|
||||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub)));
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub)));
|
||||||
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled)
|
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled && displayedMission.Completed)
|
||||||
{
|
{
|
||||||
var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(totalReward));
|
var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(totalReward));
|
||||||
if (share > 0)
|
if (share > 0)
|
||||||
|
|||||||
@@ -575,7 +575,7 @@ namespace Barotrauma
|
|||||||
//cancel dragging if too far away from the container of the dragged item
|
//cancel dragging if too far away from the container of the dragged item
|
||||||
if (DraggingItems.Any())
|
if (DraggingItems.Any())
|
||||||
{
|
{
|
||||||
var rootContainer = DraggingItems.First().GetRootContainer();
|
var rootContainer = DraggingItems.First().RootContainer;
|
||||||
var rootInventory = DraggingItems.First().ParentInventory;
|
var rootInventory = DraggingItems.First().ParentInventory;
|
||||||
|
|
||||||
if (rootContainer != null)
|
if (rootContainer != null)
|
||||||
|
|||||||
@@ -92,9 +92,6 @@ namespace Barotrauma.Items.Components
|
|||||||
rect.Height = (int)(rect.Height * (1.0f - openState));
|
rect.Height = (int)(rect.Height * (1.0f - openState));
|
||||||
}
|
}
|
||||||
|
|
||||||
//only merge the door's convex hull with overlapping wall segments if it's fully open or fully closed
|
|
||||||
//it's the heaviest part of changing the convex hull, and doesn't need to be done while the door is still in motion
|
|
||||||
bool mergeOverlappingSegments = openState <= 0.0f || openState >= 1.0f;
|
|
||||||
if (Window.Height > 0 && Window.Width > 0)
|
if (Window.Height > 0 && Window.Width > 0)
|
||||||
{
|
{
|
||||||
if (IsHorizontal)
|
if (IsHorizontal)
|
||||||
@@ -117,7 +114,7 @@ namespace Barotrauma.Items.Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
convexHull2.Enabled = true;
|
convexHull2.Enabled = true;
|
||||||
convexHull2.SetVertices(GetConvexHullCorners(rect2), mergeOverlappingSegments);
|
SetVertices(convexHull2, rect2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,7 +138,7 @@ namespace Barotrauma.Items.Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
convexHull2.Enabled = true;
|
convexHull2.Enabled = true;
|
||||||
convexHull2.SetVertices(GetConvexHullCorners(rect2), mergeOverlappingSegments);
|
SetVertices(convexHull2, rect2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,11 +153,23 @@ namespace Barotrauma.Items.Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
convexHull.Enabled = true;
|
convexHull.Enabled = true;
|
||||||
convexHull.SetVertices(GetConvexHullCorners(rect), mergeOverlappingSegments);
|
SetVertices(convexHull, rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void SetVertices(ConvexHull convexHull, Rectangle rect)
|
||||||
|
{
|
||||||
|
var verts = GetConvexHullCorners(rect);
|
||||||
|
Vector2 center = (verts[0] + verts[2]) / 2;
|
||||||
|
convexHull.SetVertices(
|
||||||
|
verts,
|
||||||
|
IsHorizontal ?
|
||||||
|
new Vector2[] { new Vector2(verts[0].X, center.Y), new Vector2(verts[2].X, center.Y) } :
|
||||||
|
new Vector2[] { new Vector2(center.X, verts[0].Y), new Vector2(center.X, verts[2].Y) });
|
||||||
|
convexHull.MaxMergeLosVerticesDist = 35.0f;
|
||||||
|
}
|
||||||
|
|
||||||
partial void UpdateProjSpecific(float deltaTime)
|
partial void UpdateProjSpecific(float deltaTime)
|
||||||
{
|
{
|
||||||
if (shakeTimer > 0.0f)
|
if (shakeTimer > 0.0f)
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||||
{
|
{
|
||||||
if (character == null || !character.IsKeyDown(InputType.Aim)) { return; }
|
if (character == null || !character.IsKeyDown(InputType.Aim) || !character.CanAim) { return; }
|
||||||
|
|
||||||
//camera focused on some other item/device, don't draw the crosshair
|
//camera focused on some other item/device, don't draw the crosshair
|
||||||
if (character.ViewTarget != null && (character.ViewTarget is Item viewTargetItem) && viewTargetItem.Prefab.FocusOnSelected) { return; }
|
if (character.ViewTarget != null && (character.ViewTarget is Item viewTargetItem) && viewTargetItem.Prefab.FocusOnSelected) { return; }
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
//whole text can fit in the textblock, no need to scroll
|
//whole text can fit in the textblock, no need to scroll
|
||||||
needsScrolling = false;
|
needsScrolling = false;
|
||||||
scrollingText = DisplayText.Value;
|
TextBlock.Text = scrollingText = DisplayText.Value;
|
||||||
scrollPadding = 0;
|
scrollPadding = 0;
|
||||||
scrollAmount = 0.0f;
|
scrollAmount = 0.0f;
|
||||||
scrollIndex = 0;
|
scrollIndex = 0;
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ namespace Barotrauma.Items.Components
|
|||||||
Step = 0.05f,
|
Step = 0.05f,
|
||||||
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||||
{
|
{
|
||||||
|
lastReceivedTargetForce = null;
|
||||||
float newTargetForce = barScroll * 200.0f - 100.0f;
|
float newTargetForce = barScroll * 200.0f - 100.0f;
|
||||||
if (Math.Abs(newTargetForce - targetForce) < 0.01) { return false; }
|
if (Math.Abs(newTargetForce - targetForce) < 0.01) { return false; }
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Barotrauma.Items.Components
|
|||||||
private GUIFrame selectedItemFrame;
|
private GUIFrame selectedItemFrame;
|
||||||
private GUIFrame selectedItemReqsFrame;
|
private GUIFrame selectedItemReqsFrame;
|
||||||
|
|
||||||
private GUITextBlock amountTextMin, amountTextMax;
|
private GUITextBlock amountTextMax;
|
||||||
private GUIScrollBar amountInput;
|
private GUIScrollBar amountInput;
|
||||||
|
|
||||||
public GUIButton ActivateButton
|
public GUIButton ActivateButton
|
||||||
@@ -29,6 +29,9 @@ namespace Barotrauma.Items.Components
|
|||||||
private GUIComponent outputSlot;
|
private GUIComponent outputSlot;
|
||||||
private GUIComponent inputInventoryHolder, outputInventoryHolder;
|
private GUIComponent inputInventoryHolder, outputInventoryHolder;
|
||||||
|
|
||||||
|
private readonly List<GUIButton> itemCategoryButtons = new List<GUIButton>();
|
||||||
|
private MapEntityCategory? selectedItemCategory;
|
||||||
|
|
||||||
public FabricationRecipe SelectedItem
|
public FabricationRecipe SelectedItem
|
||||||
{
|
{
|
||||||
get { return selectedItem; }
|
get { return selectedItem; }
|
||||||
@@ -77,7 +80,67 @@ namespace Barotrauma.Items.Components
|
|||||||
AutoScaleVertical = true
|
AutoScaleVertical = true
|
||||||
};
|
};
|
||||||
|
|
||||||
var mainFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.95f), paddedFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
|
var innerArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.95f), paddedFrame.RectTransform, Anchor.Center), isHorizontal: true)
|
||||||
|
{
|
||||||
|
RelativeSpacing = 0.01f,
|
||||||
|
Stretch = true,
|
||||||
|
CanBeFocused = true
|
||||||
|
};
|
||||||
|
|
||||||
|
List<MapEntityCategory> itemCategories = Enum.GetValues<MapEntityCategory>().ToList();
|
||||||
|
itemCategories.Remove(MapEntityCategory.None);
|
||||||
|
itemCategories.RemoveAll(c => fabricationRecipes.None(f => f.Value?.TargetItem is ItemPrefab ti && ti.Category.HasFlag(c)));
|
||||||
|
itemCategoryButtons.Clear();
|
||||||
|
|
||||||
|
//only create category buttons if there's more than one category in addition to "All"
|
||||||
|
if (itemCategories.Count > 2)
|
||||||
|
{
|
||||||
|
// === Item category buttons ===
|
||||||
|
var categoryButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.05f, 1.0f), innerArea.RectTransform))
|
||||||
|
{
|
||||||
|
RelativeSpacing = 0.01f
|
||||||
|
};
|
||||||
|
|
||||||
|
int buttonSize = Math.Min(categoryButtonContainer.Rect.Width, categoryButtonContainer.Rect.Height / itemCategories.Count);
|
||||||
|
|
||||||
|
var categoryButton = new GUIButton(new RectTransform(new Point(buttonSize), categoryButtonContainer.RectTransform), style: "CategoryButton.All")
|
||||||
|
{
|
||||||
|
ToolTip = TextManager.Get("MapEntityCategory.All"),
|
||||||
|
OnClicked = OnClickedCategoryButton
|
||||||
|
};
|
||||||
|
itemCategoryButtons.Add(categoryButton);
|
||||||
|
foreach (MapEntityCategory category in itemCategories)
|
||||||
|
{
|
||||||
|
categoryButton = new GUIButton(new RectTransform(new Point(buttonSize), categoryButtonContainer.RectTransform),
|
||||||
|
style: "CategoryButton." + category)
|
||||||
|
{
|
||||||
|
ToolTip = TextManager.Get("MapEntityCategory." + category),
|
||||||
|
UserData = category,
|
||||||
|
OnClicked = OnClickedCategoryButton
|
||||||
|
};
|
||||||
|
itemCategoryButtons.Add(categoryButton);
|
||||||
|
}
|
||||||
|
bool OnClickedCategoryButton(GUIButton button, object userData)
|
||||||
|
{
|
||||||
|
MapEntityCategory? newCategory = !button.Selected ? (MapEntityCategory?)userData : null;
|
||||||
|
if (newCategory.HasValue) { itemFilterBox.Text = ""; }
|
||||||
|
selectedItemCategory = newCategory;
|
||||||
|
FilterEntities(newCategory, itemFilterBox.Text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
foreach (var btn in itemCategoryButtons)
|
||||||
|
{
|
||||||
|
btn.RectTransform.SizeChanged += () =>
|
||||||
|
{
|
||||||
|
if (btn.Frame.sprites == null || !btn.Frame.sprites.TryGetValue(GUIComponent.ComponentState.None, out var spriteList)) { return; }
|
||||||
|
var sprite = spriteList?.First();
|
||||||
|
if (sprite == null) { return; }
|
||||||
|
btn.RectTransform.NonScaledSize = new Point(btn.Rect.Width, (int)(btn.Rect.Width * ((float)sprite.Sprite.SourceRect.Height / sprite.Sprite.SourceRect.Width)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mainFrame = new GUILayoutGroup(new RectTransform(Vector2.One, innerArea.RectTransform), childAnchor: Anchor.TopCenter)
|
||||||
{
|
{
|
||||||
RelativeSpacing = 0.02f,
|
RelativeSpacing = 0.02f,
|
||||||
Stretch = true,
|
Stretch = true,
|
||||||
@@ -105,10 +168,13 @@ namespace Barotrauma.Items.Components
|
|||||||
Padding = Vector4.Zero,
|
Padding = Vector4.Zero,
|
||||||
AutoScaleVertical = true
|
AutoScaleVertical = true
|
||||||
};
|
};
|
||||||
itemFilterBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), createClearButton: true);
|
itemFilterBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), createClearButton: true)
|
||||||
|
{
|
||||||
|
OverflowClip = true
|
||||||
|
};
|
||||||
itemFilterBox.OnTextChanged += (textBox, text) =>
|
itemFilterBox.OnTextChanged += (textBox, text) =>
|
||||||
{
|
{
|
||||||
FilterEntities(text);
|
FilterEntities(selectedItemCategory, text);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
filterArea.RectTransform.MaxSize = new Point(int.MaxValue, itemFilterBox.Rect.Height);
|
filterArea.RectTransform.MaxSize = new Point(int.MaxValue, itemFilterBox.Rect.Height);
|
||||||
@@ -174,7 +240,7 @@ namespace Barotrauma.Items.Components
|
|||||||
Stretch = true
|
Stretch = true
|
||||||
};
|
};
|
||||||
|
|
||||||
amountTextMin = new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), amountInputHolder.RectTransform), "1", textAlignment: Alignment.Center);
|
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), amountInputHolder.RectTransform), "1", textAlignment: Alignment.Center);
|
||||||
|
|
||||||
amountInput = new GUIScrollBar(new RectTransform(new Vector2(0.7f, 1.0f), amountInputHolder.RectTransform), barSize: 0.1f, style: "GUISlider")
|
amountInput = new GUIScrollBar(new RectTransform(new Vector2(0.7f, 1.0f), amountInputHolder.RectTransform), barSize: 0.1f, style: "GUISlider")
|
||||||
{
|
{
|
||||||
@@ -489,14 +555,36 @@ namespace Barotrauma.Items.Components
|
|||||||
inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUIStyle.Green, 0.5f, 0.5f, 0.2f);
|
inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUIStyle.Green, 0.5f, 0.5f, 0.2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
var requiredItemPrefab = requiredItem.FirstMatchingPrefab;
|
|
||||||
var itemIcon = requiredItemPrefab.InventoryIcon ?? requiredItemPrefab.Sprite;
|
|
||||||
Rectangle slotRect = inputContainer.Inventory.visualSlots[slotIndex].Rect;
|
Rectangle slotRect = inputContainer.Inventory.visualSlots[slotIndex].Rect;
|
||||||
itemIcon.Draw(
|
|
||||||
spriteBatch,
|
var requiredItemPrefab = requiredItem.FirstMatchingPrefab;
|
||||||
slotRect.Center.ToVector2(),
|
|
||||||
color: requiredItemPrefab.InventoryIconColor * 0.3f,
|
float iconAlpha = 0.0f;
|
||||||
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y));
|
ItemPrefab requiredItemToDisplay;
|
||||||
|
int count = requiredItem.ItemPrefabs.Count();
|
||||||
|
if (count > 1)
|
||||||
|
{
|
||||||
|
float iconCycleSpeed = 0.5f / count;
|
||||||
|
float iconCycleT = (float)Timing.TotalTime * iconCycleSpeed;
|
||||||
|
int iconIndex = (int)(iconCycleT % requiredItem.ItemPrefabs.Count());
|
||||||
|
|
||||||
|
requiredItemToDisplay = requiredItem.ItemPrefabs.Skip(iconIndex).FirstOrDefault();
|
||||||
|
iconAlpha = Math.Min(Math.Abs(MathF.Sin(iconCycleT * MathHelper.Pi)) * 2.0f, 1.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requiredItemToDisplay = requiredItem.ItemPrefabs.FirstOrDefault();
|
||||||
|
iconAlpha = 1.0f;
|
||||||
|
}
|
||||||
|
if (iconAlpha > 0.0f)
|
||||||
|
{
|
||||||
|
var itemIcon = requiredItemToDisplay.InventoryIcon ?? requiredItemToDisplay.Sprite;
|
||||||
|
itemIcon.Draw(
|
||||||
|
spriteBatch,
|
||||||
|
slotRect.Center.ToVector2(),
|
||||||
|
color: requiredItemToDisplay.InventoryIconColor * 0.3f * iconAlpha,
|
||||||
|
scale: Math.Min(slotRect.Width * 0.9f / itemIcon.size.X, slotRect.Height * 0.9f / itemIcon.size.Y));
|
||||||
|
}
|
||||||
|
|
||||||
if (missingCount > 1)
|
if (missingCount > 1)
|
||||||
{
|
{
|
||||||
@@ -552,7 +640,11 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
toolTipText = $"‖color:{Color.White.ToStringHex()}‖{toolTipText}‖color:end‖";
|
toolTipText = $"‖color:{Color.White.ToStringHex()}‖{toolTipText}‖color:end‖";
|
||||||
if (!requiredItemPrefab.Description.IsNullOrEmpty())
|
if (!requiredItem.OverrideDescription.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
toolTipText += '\n' + requiredItem.OverrideDescription;
|
||||||
|
}
|
||||||
|
else if (!requiredItemPrefab.Description.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
toolTipText += '\n' + requiredItemPrefab.Description;
|
toolTipText += '\n' + requiredItemPrefab.Description;
|
||||||
}
|
}
|
||||||
@@ -601,22 +693,21 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool FilterEntities(string filter)
|
private bool FilterEntities(MapEntityCategory? category, string filter)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(filter))
|
foreach (GUIComponent child in itemList.Content.Children)
|
||||||
{
|
{
|
||||||
itemList.Content.Children.ForEach(c => c.Visible = true);
|
FabricationRecipe recipe = child.UserData as FabricationRecipe;
|
||||||
}
|
if (recipe?.DisplayName == null) { continue; }
|
||||||
else
|
child.Visible =
|
||||||
{
|
(string.IsNullOrWhiteSpace(filter) || recipe.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase)) &&
|
||||||
foreach (GUIComponent child in itemList.Content.Children)
|
(!category.HasValue || recipe.TargetItem.Category.HasFlag(category.Value));
|
||||||
{
|
|
||||||
FabricationRecipe recipe = child.UserData as FabricationRecipe;
|
|
||||||
if (recipe?.DisplayName == null) { continue; }
|
|
||||||
child.Visible = recipe.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (GUIButton btn in itemCategoryButtons)
|
||||||
|
{
|
||||||
|
btn.Selected = (MapEntityCategory?)btn.UserData == selectedItemCategory;
|
||||||
|
}
|
||||||
HideEmptyItemListCategories();
|
HideEmptyItemListCategories();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -648,7 +739,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
public bool ClearFilter()
|
public bool ClearFilter()
|
||||||
{
|
{
|
||||||
FilterEntities("");
|
FilterEntities(selectedItemCategory, "");
|
||||||
itemList.UpdateScrollBarSize();
|
itemList.UpdateScrollBarSize();
|
||||||
itemList.BarScroll = 0.0f;
|
itemList.BarScroll = 0.0f;
|
||||||
itemFilterBox.Text = "";
|
itemFilterBox.Text = "";
|
||||||
@@ -737,6 +828,7 @@ namespace Barotrauma.Items.Components
|
|||||||
TextManager.Get("FabricatorRequiredSkills"), textColor: inadequateSkills.Any() ? GUIStyle.Red : GUIStyle.Green, font: GUIStyle.SubHeadingFont)
|
TextManager.Get("FabricatorRequiredSkills"), textColor: inadequateSkills.Any() ? GUIStyle.Red : GUIStyle.Green, font: GUIStyle.SubHeadingFont)
|
||||||
{
|
{
|
||||||
AutoScaleHorizontal = true,
|
AutoScaleHorizontal = true,
|
||||||
|
ToolTip = TextManager.Get("fabricatorrequiredskills.tooltip")
|
||||||
};
|
};
|
||||||
foreach (Skill skill in selectedItem.RequiredSkills)
|
foreach (Skill skill in selectedItem.RequiredSkills)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -125,18 +125,15 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
public static MiniMapSettings Default = new MiniMapSettings
|
public static MiniMapSettings Default = new MiniMapSettings
|
||||||
(
|
(
|
||||||
ignoreOutposts: false,
|
|
||||||
createHullElements: true,
|
createHullElements: true,
|
||||||
elementColor: MiniMap.MiniMapBaseColor
|
elementColor: MiniMap.MiniMapBaseColor
|
||||||
);
|
);
|
||||||
|
|
||||||
public readonly bool IgnoreOutposts;
|
|
||||||
public readonly bool CreateHullElements;
|
public readonly bool CreateHullElements;
|
||||||
public readonly Color ElementColor;
|
public readonly Color ElementColor;
|
||||||
|
|
||||||
public MiniMapSettings(bool ignoreOutposts = false, bool createHullElements = false, Color? elementColor = null)
|
public MiniMapSettings(bool createHullElements = false, Color? elementColor = null)
|
||||||
{
|
{
|
||||||
IgnoreOutposts = ignoreOutposts;
|
|
||||||
CreateHullElements = createHullElements;
|
CreateHullElements = createHullElements;
|
||||||
ElementColor = elementColor ?? MiniMap.MiniMapBaseColor;
|
ElementColor = elementColor ?? MiniMap.MiniMapBaseColor;
|
||||||
}
|
}
|
||||||
@@ -437,7 +434,11 @@ namespace Barotrauma.Items.Components
|
|||||||
prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||||
submarineContainer.ClearChildren();
|
submarineContainer.ClearChildren();
|
||||||
|
|
||||||
if (item.Submarine is null) { return; }
|
if (item.Submarine is null)
|
||||||
|
{
|
||||||
|
displayedSubs.Clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
scissorComponent = new GUIScissorComponent(new RectTransform(Vector2.One, submarineContainer.RectTransform, Anchor.Center));
|
scissorComponent = new GUIScissorComponent(new RectTransform(Vector2.One, submarineContainer.RectTransform, Anchor.Center));
|
||||||
miniMapContainer = new GUIFrame(new RectTransform(Vector2.One, scissorComponent.Content.RectTransform, Anchor.Center), style: null) { CanBeFocused = false };
|
miniMapContainer = new GUIFrame(new RectTransform(Vector2.One, scissorComponent.Content.RectTransform, Anchor.Center), style: null) { CanBeFocused = false };
|
||||||
@@ -445,8 +446,8 @@ namespace Barotrauma.Items.Components
|
|||||||
ImmutableHashSet<Item> hullPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.HiddenInGame && !it.NonInteractable && it.Prefab.ShowInStatusMonitor && (it.GetComponent<Door>() != null || it.GetComponent<Turret>() != null)).ToImmutableHashSet();
|
ImmutableHashSet<Item> hullPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.HiddenInGame && !it.NonInteractable && it.Prefab.ShowInStatusMonitor && (it.GetComponent<Door>() != null || it.GetComponent<Turret>() != null)).ToImmutableHashSet();
|
||||||
miniMapFrame = CreateMiniMap(item.Submarine, submarineContainer, MiniMapSettings.Default, hullPointsOfInterest, out hullStatusComponents);
|
miniMapFrame = CreateMiniMap(item.Submarine, submarineContainer, MiniMapSettings.Default, hullPointsOfInterest, out hullStatusComponents);
|
||||||
|
|
||||||
IEnumerable<Item> electrialPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.HiddenInGame && !it.NonInteractable && it.GetComponent<Repairable>() != null);
|
IEnumerable<Item> electricalPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.HiddenInGame && !it.NonInteractable && it.GetComponent<Repairable>() != null);
|
||||||
electricalFrame = CreateMiniMap(item.Submarine, miniMapContainer, new MiniMapSettings(createHullElements: false), electrialPointsOfInterest, out electricalMapComponents);
|
electricalFrame = CreateMiniMap(item.Submarine, miniMapContainer, new MiniMapSettings(createHullElements: false), electricalPointsOfInterest, out electricalMapComponents);
|
||||||
|
|
||||||
Dictionary<MiniMapGUIComponent, GUIComponent> electricChildren = new Dictionary<MiniMapGUIComponent, GUIComponent>();
|
Dictionary<MiniMapGUIComponent, GUIComponent> electricChildren = new Dictionary<MiniMapGUIComponent, GUIComponent>();
|
||||||
|
|
||||||
@@ -536,7 +537,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
displayedSubs.Clear();
|
displayedSubs.Clear();
|
||||||
displayedSubs.Add(item.Submarine);
|
displayedSubs.Add(item.Submarine);
|
||||||
displayedSubs.AddRange(item.Submarine.DockedTo);
|
displayedSubs.AddRange(item.Submarine.DockedTo.Where(s => s.TeamID == item.Submarine.TeamID));
|
||||||
|
|
||||||
subEntities = MapEntity.mapEntityList.Where(me => (item.Submarine is { } sub && sub.IsEntityFoundOnThisSub(me, includingConnectedSubs: true, allowDifferentType: false)) && !me.HiddenInGame).OrderByDescending(w => w.SpriteDepth).ToList();
|
subEntities = MapEntity.mapEntityList.Where(me => (item.Submarine is { } sub && sub.IsEntityFoundOnThisSub(me, includingConnectedSubs: true, allowDifferentType: false)) && !me.HiddenInGame).OrderByDescending(w => w.SpriteDepth).ToList();
|
||||||
|
|
||||||
@@ -551,7 +552,7 @@ namespace Barotrauma.Items.Components
|
|||||||
item.Submarine is { } itemSub &&
|
item.Submarine is { } itemSub &&
|
||||||
(
|
(
|
||||||
!displayedSubs.Contains(itemSub) || // current sub not displayed
|
!displayedSubs.Contains(itemSub) || // current sub not displayed
|
||||||
itemSub.DockedTo.Any(s => !displayedSubs.Contains(s) && itemSub.ConnectedDockingPorts[s].IsLocked) || // some of the docked subs not displayed
|
itemSub.DockedTo.Where(s => s.TeamID == item.Submarine.TeamID).Any(s => !displayedSubs.Contains(s) && itemSub.ConnectedDockingPorts[s].IsLocked) || // some of the docked subs not displayed
|
||||||
displayedSubs.Any(s => s != itemSub && !itemSub.DockedTo.Contains(s)) // displaying a sub that shouldn't be displayed
|
displayedSubs.Any(s => s != itemSub && !itemSub.DockedTo.Contains(s)) // displaying a sub that shouldn't be displayed
|
||||||
) ||
|
) ||
|
||||||
prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight || // resolution changed
|
prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight || // resolution changed
|
||||||
@@ -731,7 +732,7 @@ namespace Barotrauma.Items.Components
|
|||||||
if (sprite != null && ShowHullIntegrity)
|
if (sprite != null && ShowHullIntegrity)
|
||||||
{
|
{
|
||||||
Vector2 spriteSize = sprite.size;
|
Vector2 spriteSize = sprite.size;
|
||||||
Rectangle worldBorders = item.Submarine.GetDockedBorders();
|
Rectangle worldBorders = item.Submarine.GetDockedBorders(allowDifferentTeam: false);
|
||||||
worldBorders.Location += item.Submarine.WorldPosition.ToPoint();
|
worldBorders.Location += item.Submarine.WorldPosition.ToPoint();
|
||||||
foreach (Gap gap in Gap.GapList)
|
foreach (Gap gap in Gap.GapList)
|
||||||
{
|
{
|
||||||
@@ -915,7 +916,7 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RectangleF dockedBorders = item.Submarine.GetDockedBorders();
|
RectangleF dockedBorders = item.Submarine.GetDockedBorders(allowDifferentTeam: false);
|
||||||
dockedBorders.Location += item.Submarine.WorldPosition;
|
dockedBorders.Location += item.Submarine.WorldPosition;
|
||||||
RectangleF parentRect = miniMapFrame.Rect;
|
RectangleF parentRect = miniMapFrame.Rect;
|
||||||
|
|
||||||
@@ -1305,7 +1306,7 @@ namespace Barotrauma.Items.Components
|
|||||||
GameMain.Instance.GraphicsDevice.SetRenderTarget(rt);
|
GameMain.Instance.GraphicsDevice.SetRenderTarget(rt);
|
||||||
GameMain.Instance.GraphicsDevice.Clear(Color.Transparent);
|
GameMain.Instance.GraphicsDevice.Clear(Color.Transparent);
|
||||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||||
Rectangle worldBorders = sub.GetDockedBorders();
|
Rectangle worldBorders = sub.GetDockedBorders(allowDifferentTeam: false);
|
||||||
worldBorders.Location += sub.WorldPosition.ToPoint();
|
worldBorders.Location += sub.WorldPosition.ToPoint();
|
||||||
|
|
||||||
parentRect.Inflate(-inflate, -inflate);
|
parentRect.Inflate(-inflate, -inflate);
|
||||||
@@ -1526,7 +1527,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
Dictionary<MapEntity, MiniMapGUIComponent> pointsOfInterestCollection = new Dictionary<MapEntity, MiniMapGUIComponent>();
|
Dictionary<MapEntity, MiniMapGUIComponent> pointsOfInterestCollection = new Dictionary<MapEntity, MiniMapGUIComponent>();
|
||||||
|
|
||||||
RectangleF worldBorders = sub.GetDockedBorders();
|
RectangleF worldBorders = sub.GetDockedBorders(allowDifferentTeam: false);
|
||||||
worldBorders.Location += sub.WorldPosition;
|
worldBorders.Location += sub.WorldPosition;
|
||||||
|
|
||||||
// create a container that has the same "aspect ratio" as the sub
|
// create a container that has the same "aspect ratio" as the sub
|
||||||
@@ -1539,7 +1540,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
GUIFrame hullContainer = new GUIFrame(new RectTransform(containerScale * elementPadding, parent.RectTransform, Anchor.Center), style: null);
|
GUIFrame hullContainer = new GUIFrame(new RectTransform(containerScale * elementPadding, parent.RectTransform, Anchor.Center), style: null);
|
||||||
|
|
||||||
ImmutableHashSet<Submarine> connectedSubs = sub.GetConnectedSubs().ToImmutableHashSet();
|
ImmutableHashSet<Submarine> connectedSubs = sub.GetConnectedSubs().Where(s => s.TeamID == sub.TeamID).ToImmutableHashSet();
|
||||||
ImmutableArray<Hull> hullList = ImmutableArray<Hull>.Empty;
|
ImmutableArray<Hull> hullList = ImmutableArray<Hull>.Empty;
|
||||||
ImmutableDictionary<Hull, ImmutableArray<Hull>> combinedHulls = ImmutableDictionary<Hull, ImmutableArray<Hull>>.Empty;
|
ImmutableDictionary<Hull, ImmutableArray<Hull>> combinedHulls = ImmutableDictionary<Hull, ImmutableArray<Hull>>.Empty;
|
||||||
|
|
||||||
@@ -1686,7 +1687,7 @@ namespace Barotrauma.Items.Components
|
|||||||
bool IsPartofSub(MapEntity entity)
|
bool IsPartofSub(MapEntity entity)
|
||||||
{
|
{
|
||||||
if (entity.Submarine != sub && !connectedSubs.Contains(entity.Submarine) || entity.HiddenInGame) { return false; }
|
if (entity.Submarine != sub && !connectedSubs.Contains(entity.Submarine) || entity.HiddenInGame) { return false; }
|
||||||
return !settings.IgnoreOutposts || sub.IsEntityFoundOnThisSub(entity, true);
|
return sub.IsEntityFoundOnThisSub(entity, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsStandaloneHull(Hull hull)
|
bool IsStandaloneHull(Hull hull)
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ namespace Barotrauma.Items.Components
|
|||||||
private const float NearbyObjectUpdateInterval = 1.0f;
|
private const float NearbyObjectUpdateInterval = 1.0f;
|
||||||
float nearbyObjectUpdateTimer;
|
float nearbyObjectUpdateTimer;
|
||||||
|
|
||||||
private List<Submarine> connectedSubs = new List<Submarine>();
|
private readonly List<Submarine> connectedSubs = new List<Submarine>();
|
||||||
private const float ConnectedSubUpdateInterval = 1.0f;
|
private const float ConnectedSubUpdateInterval = 1.0f;
|
||||||
float connectedSubUpdateTimer;
|
float connectedSubUpdateTimer;
|
||||||
|
|
||||||
@@ -335,9 +335,11 @@ namespace Barotrauma.Items.Components
|
|||||||
// Setup layout for nav terminal
|
// Setup layout for nav terminal
|
||||||
if (isConnectedToSteering || RightLayout)
|
if (isConnectedToSteering || RightLayout)
|
||||||
{
|
{
|
||||||
|
controlContainer.RectTransform.AbsoluteOffset = Point.Zero;
|
||||||
controlContainer.RectTransform.RelativeOffset = controlBoxOffset;
|
controlContainer.RectTransform.RelativeOffset = controlBoxOffset;
|
||||||
controlContainer.RectTransform.SetPosition(Anchor.TopRight);
|
controlContainer.RectTransform.SetPosition(Anchor.TopRight);
|
||||||
sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest;
|
sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest;
|
||||||
|
if (HasMineralScanner) { PreventMineralScannerOverlap(); }
|
||||||
sonarView.RectTransform.SetPosition(Anchor.CenterLeft);
|
sonarView.RectTransform.SetPosition(Anchor.CenterLeft);
|
||||||
sonarView.RectTransform.Resize(GUISizeCalculation);
|
sonarView.RectTransform.Resize(GUISizeCalculation);
|
||||||
GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
|
GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
|
||||||
@@ -431,10 +433,11 @@ namespace Barotrauma.Items.Components
|
|||||||
var mineralScannerFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, zoomSlider.Parent.RectTransform.RelativeSize.Y), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null);
|
var mineralScannerFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, zoomSlider.Parent.RectTransform.RelativeSize.Y), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null);
|
||||||
mineralScannerSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), mineralScannerFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal")
|
mineralScannerSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), mineralScannerFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal")
|
||||||
{
|
{
|
||||||
|
Selected = UseMineralScanner,
|
||||||
OnClicked = (button, data) =>
|
OnClicked = (button, data) =>
|
||||||
{
|
{
|
||||||
useMineralScanner = !useMineralScanner;
|
UseMineralScanner = !UseMineralScanner;
|
||||||
button.Selected = useMineralScanner;
|
button.Selected = UseMineralScanner;
|
||||||
if (GameMain.Client != null)
|
if (GameMain.Client != null)
|
||||||
{
|
{
|
||||||
unsentChanges = true;
|
unsentChanges = true;
|
||||||
@@ -496,12 +499,12 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
if (transducer.Transducer.Item.Submarine == null) { continue; }
|
if (transducer.Transducer.Item.Submarine == null) { continue; }
|
||||||
if (connectedSubs.Contains(transducer.Transducer.Item.Submarine)) { continue; }
|
if (connectedSubs.Contains(transducer.Transducer.Item.Submarine)) { continue; }
|
||||||
connectedSubs = transducer.Transducer.Item.Submarine?.GetConnectedSubs();
|
connectedSubs.AddRange(transducer.Transducer.Item.Submarine.GetConnectedSubs());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (item.Submarine != null)
|
else if (item.Submarine != null)
|
||||||
{
|
{
|
||||||
connectedSubs = item.Submarine?.GetConnectedSubs();
|
connectedSubs.AddRange(item.Submarine?.GetConnectedSubs());
|
||||||
}
|
}
|
||||||
connectedSubUpdateTimer = ConnectedSubUpdateInterval;
|
connectedSubUpdateTimer = ConnectedSubUpdateInterval;
|
||||||
}
|
}
|
||||||
@@ -1032,7 +1035,7 @@ namespace Barotrauma.Items.Components
|
|||||||
missionIndex++;
|
missionIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null &&
|
if (HasMineralScanner && UseMineralScanner && CurrentMode == Mode.Active && MineralClusters != null &&
|
||||||
(item.CurrentHull == null || !DetectSubmarineWalls))
|
(item.CurrentHull == null || !DetectSubmarineWalls))
|
||||||
{
|
{
|
||||||
foreach (var c in MineralClusters)
|
foreach (var c in MineralClusters)
|
||||||
@@ -1512,9 +1515,10 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Item item in Item.ItemList)
|
foreach (Item item in Item.SonarVisibleItems)
|
||||||
{
|
{
|
||||||
if (item.CurrentHull == null && item.Prefab.SonarSize > 0.0f)
|
System.Diagnostics.Debug.Assert(item.Prefab.SonarSize > 0.0f);
|
||||||
|
if (item.CurrentHull == null)
|
||||||
{
|
{
|
||||||
float pointDist = ((item.WorldPosition - pingSource) * displayScale).LengthSquared();
|
float pointDist = ((item.WorldPosition - pingSource) * displayScale).LengthSquared();
|
||||||
if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr)
|
if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr)
|
||||||
@@ -1922,7 +1926,7 @@ namespace Barotrauma.Items.Components
|
|||||||
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
|
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
|
||||||
msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
|
msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
|
||||||
}
|
}
|
||||||
msg.WriteBoolean(useMineralScanner);
|
msg.WriteBoolean(UseMineralScanner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1934,7 +1938,7 @@ namespace Barotrauma.Items.Components
|
|||||||
float zoomT = 1.0f;
|
float zoomT = 1.0f;
|
||||||
bool directionalPing = useDirectionalPing;
|
bool directionalPing = useDirectionalPing;
|
||||||
float directionT = 0.0f;
|
float directionT = 0.0f;
|
||||||
bool mineralScanner = useMineralScanner;
|
bool mineralScanner = UseMineralScanner;
|
||||||
if (isActive)
|
if (isActive)
|
||||||
{
|
{
|
||||||
zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||||
@@ -1965,7 +1969,7 @@ namespace Barotrauma.Items.Components
|
|||||||
pingDirection = new Vector2((float)Math.Cos(pingAngle), (float)Math.Sin(pingAngle));
|
pingDirection = new Vector2((float)Math.Cos(pingAngle), (float)Math.Sin(pingAngle));
|
||||||
}
|
}
|
||||||
useDirectionalPing = directionalModeSwitch.Selected = directionalPing;
|
useDirectionalPing = directionalModeSwitch.Selected = directionalPing;
|
||||||
useMineralScanner = mineralScanner;
|
UseMineralScanner = mineralScanner;
|
||||||
if (mineralScannerSwitch != null)
|
if (mineralScannerSwitch != null)
|
||||||
{
|
{
|
||||||
mineralScannerSwitch.Selected = mineralScanner;
|
mineralScannerSwitch.Selected = mineralScanner;
|
||||||
@@ -1982,7 +1986,7 @@ namespace Barotrauma.Items.Components
|
|||||||
directionalModeSwitch.Selected = useDirectionalPing;
|
directionalModeSwitch.Selected = useDirectionalPing;
|
||||||
if (mineralScannerSwitch != null)
|
if (mineralScannerSwitch != null)
|
||||||
{
|
{
|
||||||
mineralScannerSwitch.Selected = useMineralScanner;
|
mineralScannerSwitch.Selected = UseMineralScanner;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,8 +178,9 @@ namespace Barotrauma.Items.Components
|
|||||||
var autoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.75f, 0.62f), paddedControlContainer.RectTransform, Anchor.BottomCenter), "OutlineFrame");
|
var autoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.75f, 0.62f), paddedControlContainer.RectTransform, Anchor.BottomCenter), "OutlineFrame");
|
||||||
var paddedAutoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.92f, 0.88f), autoPilotControls.RectTransform, Anchor.Center), style: null);
|
var paddedAutoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.92f, 0.88f), autoPilotControls.RectTransform, Anchor.Center), style: null);
|
||||||
|
|
||||||
|
int textLimit = (int)(paddedAutoPilotControls.Rect.Width * 0.75f);
|
||||||
maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.TopCenter),
|
maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.TopCenter),
|
||||||
TextManager.Get("SteeringMaintainPos"), font: GUIStyle.SmallFont, style: "GUIRadioButton")
|
ToolBox.LimitString(TextManager.Get("SteeringMaintainPos"), GUIStyle.SmallFont, textLimit), font: GUIStyle.SmallFont, style: "GUIRadioButton")
|
||||||
{
|
{
|
||||||
UserData = UIHighlightAction.ElementId.MaintainPosTickBox,
|
UserData = UIHighlightAction.ElementId.MaintainPosTickBox,
|
||||||
Enabled = autoPilot,
|
Enabled = autoPilot,
|
||||||
@@ -214,7 +215,6 @@ namespace Barotrauma.Items.Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
int textLimit = (int)(paddedAutoPilotControls.Rect.Width * 0.75f);
|
|
||||||
levelStartTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.Center),
|
levelStartTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.Center),
|
||||||
GameMain.GameSession?.StartLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.Name, GUIStyle.SmallFont, textLimit),
|
GameMain.GameSession?.StartLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.Name, GUIStyle.SmallFont, textLimit),
|
||||||
font: GUIStyle.SmallFont, style: "GUIRadioButton")
|
font: GUIStyle.SmallFont, style: "GUIRadioButton")
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ namespace Barotrauma.Items.Components
|
|||||||
public float FlashTimer { get; private set; }
|
public float FlashTimer { get; private set; }
|
||||||
public static Wire DraggingConnected { get; private set; }
|
public static Wire DraggingConnected { get; private set; }
|
||||||
|
|
||||||
public static void DrawConnections(SpriteBatch spriteBatch, ConnectionPanel panel, Rectangle dragArea, Character character)
|
public static void DrawConnections(SpriteBatch spriteBatch, ConnectionPanel panel, Rectangle dragArea, Character character,
|
||||||
|
out (Vector2 tooltipPos, LocalizedString text) tooltip)
|
||||||
{
|
{
|
||||||
if (DraggingConnected?.Item?.Removed ?? true)
|
if (DraggingConnected?.Item?.Removed ?? true)
|
||||||
{
|
{
|
||||||
@@ -64,6 +65,8 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tooltip = (Vector2.Zero, string.Empty);
|
||||||
|
|
||||||
//two passes: first the connector, then the wires to get the wires to render in front
|
//two passes: first the connector, then the wires to get the wires to render in front
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
@@ -97,6 +100,42 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector2 position = c.IsOutput ? rightPos : leftPos;
|
||||||
|
Color highlightColor = Color.Transparent;
|
||||||
|
if (ConnectionPanel.ShouldDebugDrawWiring)
|
||||||
|
{
|
||||||
|
if (c.IsPower)
|
||||||
|
{
|
||||||
|
highlightColor = VisualizeSignal(0.0f, highlightColor, Color.Red);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
highlightColor = VisualizeSignal(c.LastReceivedSignal.TimeSinceCreated, highlightColor, Color.LightGreen);
|
||||||
|
highlightColor = VisualizeSignal(c.LastSentSignal.TimeSinceCreated, highlightColor, Color.Orange);
|
||||||
|
}
|
||||||
|
bool mouseOn = Vector2.DistanceSquared(position, PlayerInput.MousePosition) < MathUtils.Pow2(35 * GUI.Scale);
|
||||||
|
|
||||||
|
LocalizedString toolTipText = c.GetToolTip();
|
||||||
|
if (mouseOn) { tooltip = (position, toolTipText); }
|
||||||
|
if (!toolTipText.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
var glowSprite = GUIStyle.UIGlowCircular.Value.Sprite;
|
||||||
|
glowSprite.Draw(spriteBatch, position, highlightColor, glowSprite.size / 2,
|
||||||
|
scale: 45.0f / glowSprite.size.X * panel.Scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Color VisualizeSignal(double timeSinceCreated, Color defaultColor, Color color)
|
||||||
|
{
|
||||||
|
if (timeSinceCreated < 1.0f)
|
||||||
|
{
|
||||||
|
float pulseAmount = (MathF.Sin((float)Timing.TotalTimeUnpaused * 10.0f) + 3.0f) / 4.0f;
|
||||||
|
Color targetColor = Color.Lerp(defaultColor, color, pulseAmount);
|
||||||
|
return Color.Lerp(targetColor, defaultColor, (float)timeSinceCreated);
|
||||||
|
}
|
||||||
|
return defaultColor;
|
||||||
|
}
|
||||||
|
|
||||||
//outputs are drawn at the right side of the panel, inputs at the left
|
//outputs are drawn at the right side of the panel, inputs at the left
|
||||||
if (c.IsOutput)
|
if (c.IsOutput)
|
||||||
{
|
{
|
||||||
@@ -127,7 +166,6 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (DraggingConnected != null)
|
if (DraggingConnected != null)
|
||||||
{
|
{
|
||||||
if (mouseInRect)
|
if (mouseInRect)
|
||||||
@@ -225,7 +263,9 @@ namespace Barotrauma.Items.Components
|
|||||||
GUI.DrawString(spriteBatch, labelPos, text, GUIStyle.TextColorBright, font: GUIStyle.SmallFont);
|
GUI.DrawString(spriteBatch, labelPos, text, GUIStyle.TextColorBright, font: GUIStyle.SmallFont);
|
||||||
|
|
||||||
float connectorSpriteScale = (35.0f / connectionSprite.SourceRect.Width) * panel.Scale;
|
float connectorSpriteScale = (35.0f / connectionSprite.SourceRect.Width) * panel.Scale;
|
||||||
|
|
||||||
connectionSprite.Draw(spriteBatch, position, scale: connectorSpriteScale);
|
connectionSprite.Draw(spriteBatch, position, scale: connectorSpriteScale);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawWires(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 wirePosition, bool mouseIn, Wire equippedWire, float wireInterval)
|
private void DrawWires(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 wirePosition, bool mouseIn, Wire equippedWire, float wireInterval)
|
||||||
@@ -259,7 +299,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
bool alreadyConnected = DraggingConnected.IsConnectedTo(panel.Item);
|
bool alreadyConnected = DraggingConnected.IsConnectedTo(panel.Item);
|
||||||
DraggingConnected.RemoveConnection(panel.Item);
|
DraggingConnected.RemoveConnection(panel.Item);
|
||||||
if (DraggingConnected.Connect(this, !alreadyConnected, true))
|
if (DraggingConnected.TryConnect(this, !alreadyConnected, true))
|
||||||
{
|
{
|
||||||
var otherConnection = DraggingConnected.OtherConnection(this);
|
var otherConnection = DraggingConnected.OtherConnection(this);
|
||||||
ConnectWire(DraggingConnected);
|
ConnectWire(DraggingConnected);
|
||||||
@@ -307,6 +347,63 @@ namespace Barotrauma.Items.Components
|
|||||||
FlashTimer -= deltaTime;
|
FlashTimer -= deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (string signal, LocalizedString tooltip) lastSignalToolTip;
|
||||||
|
private (int powerValue, LocalizedString tooltip) lastPowerToolTip;
|
||||||
|
|
||||||
|
private LocalizedString GetToolTip()
|
||||||
|
{
|
||||||
|
if (LastReceivedSignal.TimeSinceCreated < 1.0f)
|
||||||
|
{
|
||||||
|
return getSignalTooltip(LastReceivedSignal, "receivedsignal");
|
||||||
|
}
|
||||||
|
else if (LastSentSignal.TimeSinceCreated < 1.0f)
|
||||||
|
{
|
||||||
|
return getSignalTooltip(LastSentSignal, "sentsignal");
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalizedString getSignalTooltip(Signal signal, string textTag)
|
||||||
|
{
|
||||||
|
if (lastSignalToolTip.signal == signal.value && !lastSignalToolTip.tooltip.IsNullOrEmpty()) { return lastSignalToolTip.tooltip; }
|
||||||
|
lastSignalToolTip = (signal.value, TextManager.GetWithVariable(textTag, "[signal]", signal.value));
|
||||||
|
return lastSignalToolTip.tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsPower)
|
||||||
|
{
|
||||||
|
if (item.GetComponent<Powered>() is Powered powered)
|
||||||
|
{
|
||||||
|
if (IsOutput)
|
||||||
|
{
|
||||||
|
if (powered.CurrPowerConsumption < 0)
|
||||||
|
{
|
||||||
|
return getPowerTooltip(-(int)powered.CurrPowerConsumption, "reactoroutput");
|
||||||
|
}
|
||||||
|
else if (powered is PowerTransfer || powered is PowerContainer)
|
||||||
|
{
|
||||||
|
return getPowerTooltip((int)(Grid?.Power ?? 0), "reactoroutput");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!IsOutput)
|
||||||
|
{
|
||||||
|
float powerConsumption = powered.GetCurrentPowerConsumption(this);
|
||||||
|
if (!MathUtils.NearlyEqual((int)powerConsumption, 0.0f))
|
||||||
|
{
|
||||||
|
return getPowerTooltip((int)powerConsumption, "reactorload");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalizedString getPowerTooltip(int powerValue, string textTag)
|
||||||
|
{
|
||||||
|
if (lastPowerToolTip.powerValue == powerValue && !lastPowerToolTip.tooltip.IsNullOrEmpty()) { return lastPowerToolTip.tooltip; }
|
||||||
|
lastPowerToolTip = (powerValue, TextManager.GetWithVariable(textTag, "[kw]", powerValue.ToString()));
|
||||||
|
return lastPowerToolTip.tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static void DrawWire(SpriteBatch spriteBatch, Wire wire, Vector2 end, Vector2 start, Wire equippedWire, ConnectionPanel panel, LocalizedString label)
|
private static void DrawWire(SpriteBatch spriteBatch, Wire wire, Vector2 end, Vector2 start, Wire equippedWire, ConnectionPanel panel, LocalizedString label)
|
||||||
{
|
{
|
||||||
int textX = (int)start.X;
|
int textX = (int)start.X;
|
||||||
|
|||||||
+10
-3
@@ -10,6 +10,10 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
partial class ConnectionPanel : ItemComponent, IServerSerializable, IClientSerializable
|
partial class ConnectionPanel : ItemComponent, IServerSerializable, IClientSerializable
|
||||||
{
|
{
|
||||||
|
public static bool DebugWiringMode;
|
||||||
|
public static double DebugWiringEnabledUntil;
|
||||||
|
public static bool ShouldDebugDrawWiring => DebugWiringMode || Timing.TotalTimeUnpaused < DebugWiringEnabledUntil;
|
||||||
|
|
||||||
//how long the rewiring sound plays after doing changes to the wiring
|
//how long the rewiring sound plays after doing changes to the wiring
|
||||||
const float RewireSoundDuration = 5.0f;
|
const float RewireSoundDuration = 5.0f;
|
||||||
|
|
||||||
@@ -120,12 +124,15 @@ namespace Barotrauma.Items.Components
|
|||||||
if (user != Character.Controlled || user == null) { return; }
|
if (user != Character.Controlled || user == null) { return; }
|
||||||
|
|
||||||
HighlightedWire = null;
|
HighlightedWire = null;
|
||||||
Connection.DrawConnections(spriteBatch, this, dragArea.Rect, user);
|
Connection.DrawConnections(spriteBatch, this, dragArea.Rect, user, out (Vector2 tooltipPos, LocalizedString text) tooltip);
|
||||||
|
|
||||||
foreach (UISprite sprite in GUIStyle.GetComponentStyle("ConnectionPanelFront").Sprites[GUIComponent.ComponentState.None])
|
foreach (UISprite sprite in GUIStyle.GetComponentStyle("ConnectionPanelFront").Sprites[GUIComponent.ComponentState.None])
|
||||||
{
|
{
|
||||||
sprite.Draw(spriteBatch, GuiFrame.Rect, Color.White, SpriteEffects.None);
|
sprite.Draw(spriteBatch, GuiFrame.Rect, Color.White, SpriteEffects.None);
|
||||||
}
|
}
|
||||||
|
if (!tooltip.text.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
GUIComponent.DrawToolTip(spriteBatch, tooltip.text, tooltip.tooltipPos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckForLabelOverlap()
|
private void CheckForLabelOverlap()
|
||||||
@@ -225,7 +232,7 @@ namespace Barotrauma.Items.Components
|
|||||||
foreach (var wire in newWires.Where(w => !connection.Wires.Contains(w)).ToArray())
|
foreach (var wire in newWires.Where(w => !connection.Wires.Contains(w)).ToArray())
|
||||||
{
|
{
|
||||||
connection.ConnectWire(wire);
|
connection.ConnectWire(wire);
|
||||||
wire.Connect(connection, false);
|
wire.TryConnect(connection, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Microsoft.Xna.Framework.Input;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Items.Components
|
namespace Barotrauma.Items.Components
|
||||||
{
|
{
|
||||||
@@ -119,6 +118,13 @@ namespace Barotrauma.Items.Components
|
|||||||
get { return sectionExtents; }
|
get { return sectionExtents; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly record struct VisualSignal(
|
||||||
|
float TimeSent,
|
||||||
|
Color Color,
|
||||||
|
int Direction);
|
||||||
|
|
||||||
|
private VisualSignal lastReceivedSignal;
|
||||||
|
|
||||||
public static Wire DraggingWire
|
public static Wire DraggingWire
|
||||||
{
|
{
|
||||||
get => draggingWire;
|
get => draggingWire;
|
||||||
@@ -126,13 +132,11 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
public static Sprite ExtractWireSprite(ContentXElement element)
|
public static Sprite ExtractWireSprite(ContentXElement element)
|
||||||
{
|
{
|
||||||
if (defaultWireSprite == null)
|
defaultWireSprite ??=
|
||||||
{
|
new Sprite("Content/Items/Electricity/signalcomp.png", new Rectangle(970, 47, 14, 16), new Vector2(0.5f, 0.5f))
|
||||||
defaultWireSprite = new Sprite("Content/Items/Electricity/signalcomp.png", new Rectangle(970, 47, 14, 16), new Vector2(0.5f, 0.5f))
|
|
||||||
{
|
{
|
||||||
Depth = 0.855f
|
Depth = 0.855f
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
Sprite overrideSprite = null;
|
Sprite overrideSprite = null;
|
||||||
foreach (var subElement in element.Elements())
|
foreach (var subElement in element.Elements())
|
||||||
@@ -153,6 +157,35 @@ namespace Barotrauma.Items.Components
|
|||||||
if (wireSprite != defaultWireSprite) { overrideSprite = wireSprite; }
|
if (wireSprite != defaultWireSprite) { overrideSprite = wireSprite; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RegisterSignal(Signal signal, Connection source)
|
||||||
|
{
|
||||||
|
lastReceivedSignal = new VisualSignal(
|
||||||
|
(float)Timing.TotalTimeUnpaused,
|
||||||
|
GetSignalColor(signal),
|
||||||
|
Direction: source == connections[0] ? 1 : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Color[] dataSignalColors = new Color[] { Color.White, Color.LightBlue, Color.CornflowerBlue, Color.Blue, Color.BlueViolet, Color.Violet };
|
||||||
|
|
||||||
|
private static Color GetSignalColor(Signal signal)
|
||||||
|
{
|
||||||
|
if (signal.value == "0")
|
||||||
|
{
|
||||||
|
return Color.Red;
|
||||||
|
}
|
||||||
|
else if (signal.value == "1")
|
||||||
|
{
|
||||||
|
return Color.LightGreen;
|
||||||
|
}
|
||||||
|
else if (float.TryParse(signal.value, out float floatValue))
|
||||||
|
{
|
||||||
|
//convert numeric values to a color (guessing the value might be somewhere in the range of 0-200)
|
||||||
|
//so a player with a keen eye can get some info out of the color of the signal
|
||||||
|
return ToolBox.GradientLerp(Math.Abs(floatValue / 200.0f), dataSignalColors);
|
||||||
|
}
|
||||||
|
return Color.LightBlue;
|
||||||
|
}
|
||||||
|
|
||||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||||
{
|
{
|
||||||
Draw(spriteBatch, editing, Vector2.Zero, itemDepth);
|
Draw(spriteBatch, editing, Vector2.Zero, itemDepth);
|
||||||
@@ -166,20 +199,7 @@ namespace Barotrauma.Items.Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 drawOffset = Vector2.Zero;
|
Vector2 drawOffset = GetDrawOffset() + offset;
|
||||||
Submarine sub = item.Submarine;
|
|
||||||
if (IsActive && sub == null) // currently being rewired, we need to get the sub from the connections in case the wire has been taken outside
|
|
||||||
{
|
|
||||||
if (connections[0] != null && connections[0].Item.Submarine != null) { sub = connections[0].Item.Submarine; }
|
|
||||||
if (connections[1] != null && connections[1].Item.Submarine != null) { sub = connections[1].Item.Submarine; }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sub != null)
|
|
||||||
{
|
|
||||||
drawOffset = sub.DrawPosition + sub.HiddenSubPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
drawOffset += offset;
|
|
||||||
|
|
||||||
float baseDepth = UseSpriteDepth ? item.SpriteDepth : wireSprite.Depth;
|
float baseDepth = UseSpriteDepth ? item.SpriteDepth : wireSprite.Depth;
|
||||||
float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : baseDepth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
|
float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : baseDepth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
|
||||||
@@ -188,7 +208,9 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
foreach (WireSection section in sections)
|
foreach (WireSection section in sections)
|
||||||
{
|
{
|
||||||
section.Draw(spriteBatch, wireSprite, Screen.Selected == GameMain.GameScreen ? higlightColor : editorHighlightColor, drawOffset, depth + 0.00001f, Width * 2.0f);
|
section.Draw(spriteBatch, wireSprite,
|
||||||
|
Screen.Selected == GameMain.GameScreen ? higlightColor : editorHighlightColor,
|
||||||
|
drawOffset, depth + 0.00001f, Width * 2.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (item.IsSelected)
|
else if (item.IsSelected)
|
||||||
@@ -270,6 +292,12 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (ConnectionPanel.ShouldDebugDrawWiring)
|
||||||
|
{
|
||||||
|
DebugDraw(spriteBatch, alpha: 0.2f);
|
||||||
|
}
|
||||||
|
|
||||||
if (!editing || !GameMain.SubEditorScreen.WiringMode) { return; }
|
if (!editing || !GameMain.SubEditorScreen.WiringMode) { return; }
|
||||||
|
|
||||||
for (int i = 0; i < nodes.Count; i++)
|
for (int i = 0; i < nodes.Count; i++)
|
||||||
@@ -295,6 +323,102 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DebugDraw(SpriteBatch spriteBatch, float alpha = 1.0f)
|
||||||
|
{
|
||||||
|
if (sections.Count == 0 || Hidden)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float PowerPulseSpeedLow = 5.0f;
|
||||||
|
const float PowerPulseSpeedHigh = 10.0f;
|
||||||
|
const float PowerHighlightScaleLow = 1.5f;
|
||||||
|
const float PowerHighlightScaleHigh = 2.5f;
|
||||||
|
|
||||||
|
const float SignalIndicatorInterval = 15.0f;
|
||||||
|
const float SignalIndicatorSpeed = 100.0f;
|
||||||
|
|
||||||
|
Vector2 drawOffset = GetDrawOffset();
|
||||||
|
|
||||||
|
Color currentHighlightColor = Color.Transparent;
|
||||||
|
float highlightScale = 0.0f;
|
||||||
|
if (connections[0] != null && connections[1] != null)
|
||||||
|
{
|
||||||
|
float voltage = Math.Max(GetVoltage(0), GetVoltage(1));
|
||||||
|
float GetVoltage(int connectionIndex)
|
||||||
|
{
|
||||||
|
var connection1 = connections[connectionIndex];
|
||||||
|
var connection2 = connections[1 - connectionIndex];
|
||||||
|
if (connection1.IsOutput && connection1.Grid is { Power: > 0.01f } grid1)
|
||||||
|
{
|
||||||
|
if (connection2.Item.GetComponent<Powered>() is Powered powered &&
|
||||||
|
(powered.GetCurrentPowerConsumption(connection2) > 0 || powered is PowerTransfer))
|
||||||
|
{
|
||||||
|
return grid1.Voltage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
if (voltage > 0.0f)
|
||||||
|
{
|
||||||
|
//pulse faster when there's overvoltage
|
||||||
|
float pulseSpeed = voltage > 1.2f ? PowerPulseSpeedHigh : PowerPulseSpeedLow;
|
||||||
|
float pulseAmount = (MathF.Sin((float)Timing.TotalTimeUnpaused * pulseSpeed) + 1.5f) / 2.5f;
|
||||||
|
voltage = Math.Min(voltage, 1.0f);
|
||||||
|
highlightScale = MathHelper.Lerp(PowerHighlightScaleLow, PowerHighlightScaleHigh, voltage);
|
||||||
|
currentHighlightColor = Color.Red * voltage * pulseAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (highlightScale > 0.0f)
|
||||||
|
{
|
||||||
|
foreach (WireSection section in sections)
|
||||||
|
{
|
||||||
|
section.Draw(spriteBatch, wireSprite, currentHighlightColor * alpha, drawOffset, 0.0f, Width * highlightScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float signalDuration = (float)Timing.TotalTimeUnpaused - lastReceivedSignal.TimeSent;
|
||||||
|
if (ConnectionPanel.ShouldDebugDrawWiring && signalDuration < 1.0f)
|
||||||
|
{
|
||||||
|
//make some wires "off sync" so it's easier to differentiate signals on overlapping wires
|
||||||
|
float offset = item.ID % 2 == 1 ? SignalIndicatorInterval / 2 : 0.0f;
|
||||||
|
float signalProgress = ((float)(Timing.TotalTimeUnpaused * SignalIndicatorSpeed + offset) % SignalIndicatorInterval) * lastReceivedSignal.Direction;
|
||||||
|
foreach (WireSection section in sections)
|
||||||
|
{
|
||||||
|
for (float x = 0; x < section.Length; x += SignalIndicatorInterval)
|
||||||
|
{
|
||||||
|
Vector2 dir = (section.End - section.Start) / section.Length;
|
||||||
|
float posOnSection = x + signalProgress;
|
||||||
|
if (posOnSection < 0 || posOnSection > section.Length) { continue; }
|
||||||
|
Vector2 signalPos = section.Start + drawOffset + dir * posOnSection;
|
||||||
|
float a = 1.0f - Vector2.Distance(Screen.Selected.Cam.WorldViewCenter, signalPos) / 500.0f;
|
||||||
|
if (a < 0) { continue; }
|
||||||
|
signalPos.Y = -signalPos.Y;
|
||||||
|
GUI.DrawRectangle(spriteBatch, signalPos - Vector2.One * 2.5f, Vector2.One * 5, lastReceivedSignal.Color * a * (1.0f - signalDuration) * alpha, isFilled: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 GetDrawOffset()
|
||||||
|
{
|
||||||
|
Submarine sub = item.Submarine;
|
||||||
|
if (IsActive && sub == null) // currently being rewired, we need to get the sub from the connections in case the wire has been taken outside
|
||||||
|
{
|
||||||
|
if (connections[0] != null && connections[0].Item.Submarine != null) { sub = connections[0].Item.Submarine; }
|
||||||
|
if (connections[1] != null && connections[1].Item.Submarine != null) { sub = connections[1].Item.Submarine; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub == null)
|
||||||
|
{
|
||||||
|
return Vector2.Zero;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return sub.DrawPosition + sub.HiddenSubPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawHangingWire(SpriteBatch spriteBatch, Vector2 start, float depth)
|
private void DrawHangingWire(SpriteBatch spriteBatch, Vector2 start, float depth)
|
||||||
{
|
{
|
||||||
float angle = (float)Math.Sin(GameMain.GameScreen.GameTime * 2.0f + item.ID) * 0.2f;
|
float angle = (float)Math.Sin(GameMain.GameScreen.GameTime * 2.0f + item.ID) * 0.2f;
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ namespace Barotrauma.Items.Components
|
|||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serialize(false, IsPropertySaveable.No)]
|
||||||
|
public bool DebugWiring
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
[Serialize(true, IsPropertySaveable.No)]
|
[Serialize(true, IsPropertySaveable.No)]
|
||||||
public bool ShowDeadCharacters
|
public bool ShowDeadCharacters
|
||||||
{
|
{
|
||||||
@@ -111,6 +118,11 @@ namespace Barotrauma.Items.Components
|
|||||||
refEntity = item;
|
refEntity = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (equipper != null && equipper == Character.Controlled && DebugWiring)
|
||||||
|
{
|
||||||
|
ConnectionPanel.DebugWiringEnabledUntil = Timing.TotalTimeUnpaused + 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
thermalEffectState += deltaTime;
|
thermalEffectState += deltaTime;
|
||||||
thermalEffectState %= 10000.0f;
|
thermalEffectState %= 10000.0f;
|
||||||
|
|
||||||
@@ -153,6 +165,11 @@ namespace Barotrauma.Items.Components
|
|||||||
IsActive = false;
|
IsActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Drop(Character dropper, bool setTransform = true)
|
||||||
|
{
|
||||||
|
Unequip(dropper);
|
||||||
|
}
|
||||||
|
|
||||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||||
{
|
{
|
||||||
if (character == null) { return; }
|
if (character == null) { return; }
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Barotrauma.Networking;
|
using Barotrauma.Lights;
|
||||||
|
using Barotrauma.Networking;
|
||||||
using FarseerPhysics;
|
using FarseerPhysics;
|
||||||
using FarseerPhysics.Collision;
|
using FarseerPhysics.Collision;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
@@ -10,6 +11,8 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
private GUIMessageBox autodockingVerification;
|
private GUIMessageBox autodockingVerification;
|
||||||
|
|
||||||
|
private readonly ConvexHull[] convexHulls = new ConvexHull[2];
|
||||||
|
|
||||||
public Vector2 DrawSize
|
public Vector2 DrawSize
|
||||||
{
|
{
|
||||||
//use the extents of the item as the draw size
|
//use the extents of the item as the draw size
|
||||||
@@ -109,6 +112,15 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
partial void RemoveConvexHulls()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < convexHulls.Length; i++)
|
||||||
|
{
|
||||||
|
convexHulls[i]?.Remove();
|
||||||
|
convexHulls[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||||
{
|
{
|
||||||
bool isDocked = msg.ReadBoolean();
|
bool isDocked = msg.ReadBoolean();
|
||||||
|
|||||||
@@ -9,15 +9,17 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
private LightSource lightSource;
|
private LightSource lightSource;
|
||||||
|
|
||||||
partial void UpdateProjSpecific(float growModifier)
|
private float particleTimer;
|
||||||
|
|
||||||
|
partial void UpdateProjSpecific(float growModifier, float deltaTime)
|
||||||
{
|
{
|
||||||
if (this is DummyFireSource)
|
if (this is DummyFireSource)
|
||||||
{
|
{
|
||||||
EmitParticles(size, WorldPosition, hull, growModifier, null);
|
EmitParticles(size, WorldPosition, deltaTime, hull, growModifier, null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitParticles(size, WorldPosition, hull, growModifier, OnChangeHull);
|
EmitParticles(size, WorldPosition, deltaTime, hull, growModifier, OnChangeHull);
|
||||||
}
|
}
|
||||||
|
|
||||||
lightSource.Color = new Color(1.0f, 0.45f, 0.3f) * Rand.Range(0.8f, 1.0f);
|
lightSource.Color = new Color(1.0f, 0.45f, 0.3f) * Rand.Range(0.8f, 1.0f);
|
||||||
@@ -25,15 +27,21 @@ namespace Barotrauma
|
|||||||
if (Vector2.DistanceSquared(lightSource.Position, position) > 5.0f) { lightSource.Position = position + Vector2.UnitY * 30.0f; }
|
if (Vector2.DistanceSquared(lightSource.Position, position) > 5.0f) { lightSource.Position = position + Vector2.UnitY * 30.0f; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitParticles(Vector2 size, Vector2 worldPosition, Hull hull, float growModifier, Particle.OnChangeHullHandler onChangeHull = null)
|
public void EmitParticles(Vector2 size, Vector2 worldPosition, float deltaTime, Hull hull, float growModifier, Particle.OnChangeHullHandler onChangeHull = null)
|
||||||
{
|
{
|
||||||
float particleCount = Rand.Range(0.0f, size.X / 50.0f);
|
var particlePrefab = ParticleManager.FindPrefab("flame");
|
||||||
|
if (particlePrefab == null) { return; }
|
||||||
|
|
||||||
for (int i = 0; i < particleCount; i++)
|
float particlesPerSecond = MathHelper.Clamp(size.X / 2.0f, 10.0f, 200.0f);
|
||||||
|
|
||||||
|
float particleInterval = 1.0f / particlesPerSecond;
|
||||||
|
particleTimer += deltaTime;
|
||||||
|
while (particleTimer > particleInterval)
|
||||||
{
|
{
|
||||||
|
particleTimer -= particleInterval;
|
||||||
Vector2 particlePos = new Vector2(
|
Vector2 particlePos = new Vector2(
|
||||||
worldPosition.X + Rand.Range(0.0f, size.X),
|
worldPosition.X + Rand.Range(0.0f, size.X),
|
||||||
Rand.Range(worldPosition.Y - size.Y, worldPosition.Y + 20.0f));
|
worldPosition.Y - size.Y + particlePrefab.CollisionRadius);
|
||||||
|
|
||||||
Vector2 particleVel = new Vector2(
|
Vector2 particleVel = new Vector2(
|
||||||
particlePos.X - (worldPosition.X + size.X / 2.0f),
|
particlePos.X - (worldPosition.X + size.X / 2.0f),
|
||||||
@@ -41,7 +49,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
particleVel.X = MathHelper.Clamp(particleVel.X, -200.0f, 200.0f);
|
particleVel.X = MathHelper.Clamp(particleVel.X, -200.0f, 200.0f);
|
||||||
|
|
||||||
var particle = GameMain.ParticleManager.CreateParticle("flame",
|
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab,
|
||||||
particlePos, particleVel, 0.0f, hull);
|
particlePos, particleVel, 0.0f, hull);
|
||||||
|
|
||||||
if (particle == null) { continue; }
|
if (particle == null) { continue; }
|
||||||
@@ -54,7 +62,7 @@ namespace Barotrauma
|
|||||||
if (Rand.Int(5) == 1)
|
if (Rand.Int(5) == 1)
|
||||||
{
|
{
|
||||||
var smokeParticle = GameMain.ParticleManager.CreateParticle("smoke",
|
var smokeParticle = GameMain.ParticleManager.CreateParticle("smoke",
|
||||||
particlePos, new Vector2(particleVel.X, particleVel.Y * 0.1f), 0.0f, hull);
|
particlePos, new Vector2(particleVel.X, particleVel.Y * 0.1f), 0.0f, hull);
|
||||||
|
|
||||||
if (smokeParticle != null)
|
if (smokeParticle != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -324,13 +324,13 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*GUI.DrawLine(spriteBatch, new Vector2(drawRect.X, -WorldSurface), new Vector2(drawRect.Right, -WorldSurface), Color.Cyan * 0.5f);
|
GUI.DrawLine(spriteBatch, new Vector2(drawRect.X, -WorldSurface), new Vector2(drawRect.Right, -WorldSurface), Color.Cyan * 0.5f);
|
||||||
for (int i = 0; i < waveY.Length - 1; i++)
|
for (int i = 0; i < waveY.Length - 1; i++)
|
||||||
{
|
{
|
||||||
GUI.DrawLine(spriteBatch,
|
GUI.DrawLine(spriteBatch,
|
||||||
new Vector2(drawRect.X + WaveWidth * i, -WorldSurface - waveY[i] - 10),
|
new Vector2(drawRect.X + WaveWidth * i, -WorldSurface - waveY[i] - 10),
|
||||||
new Vector2(drawRect.X + WaveWidth * (i + 1), -WorldSurface - waveY[i + 1] - 10), Color.Blue * 0.5f);
|
new Vector2(drawRect.X + WaveWidth * (i + 1), -WorldSurface - waveY[i + 1] - 10), Color.Blue * 0.5f);
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (MapEntity e in linkedTo)
|
foreach (MapEntity e in linkedTo)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (var edge in cell.Edges)
|
foreach (var edge in cell.Edges)
|
||||||
{
|
{
|
||||||
if (MathUtils.GetLineIntersection(worldPosition, cell.Center, edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, out Vector2 intersection))
|
if (MathUtils.GetLineSegmentIntersection(worldPosition, cell.Center, edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, out Vector2 intersection))
|
||||||
{
|
{
|
||||||
intersectionFound = true;
|
intersectionFound = true;
|
||||||
particlePos = intersection;
|
particlePos = intersection;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Barotrauma.Extensions;
|
using Barotrauma.Items.Components;
|
||||||
using Barotrauma.Items.Components;
|
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using System;
|
using System;
|
||||||
@@ -32,13 +31,13 @@ namespace Barotrauma.Lights
|
|||||||
public bool IsHorizontal;
|
public bool IsHorizontal;
|
||||||
public bool IsAxisAligned;
|
public bool IsAxisAligned;
|
||||||
|
|
||||||
|
public Vector2 SubmarineDrawPos;
|
||||||
|
|
||||||
public Segment(SegmentPoint start, SegmentPoint end, ConvexHull convexHull)
|
public Segment(SegmentPoint start, SegmentPoint end, ConvexHull convexHull)
|
||||||
{
|
{
|
||||||
if (start.Pos.Y > end.Pos.Y)
|
if (start.Pos.Y > end.Pos.Y)
|
||||||
{
|
{
|
||||||
var temp = start;
|
(end, start) = (start, end);
|
||||||
start = end;
|
|
||||||
end = temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Start = start;
|
Start = start;
|
||||||
@@ -87,19 +86,23 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
private readonly Segment[] segments = new Segment[4];
|
private readonly Segment[] segments = new Segment[4];
|
||||||
private readonly SegmentPoint[] vertices = new SegmentPoint[4];
|
private readonly SegmentPoint[] vertices = new SegmentPoint[4];
|
||||||
private readonly SegmentPoint[] losVertices = new SegmentPoint[4];
|
private readonly SegmentPoint[] losVertices = new SegmentPoint[2];
|
||||||
private readonly VectorPair[] losOffsets = new VectorPair[4];
|
private readonly Vector2[] losOffsets = new Vector2[2];
|
||||||
|
|
||||||
private readonly bool[] backFacing;
|
|
||||||
private readonly bool[] ignoreEdge;
|
|
||||||
|
|
||||||
private readonly bool isHorizontal;
|
private readonly bool isHorizontal;
|
||||||
|
|
||||||
|
private readonly int thickness;
|
||||||
|
|
||||||
public VertexPositionColor[] ShadowVertices { get; private set; }
|
public VertexPositionColor[] ShadowVertices { get; private set; }
|
||||||
public VertexPositionTexture[] PenumbraVertices { get; private set; }
|
public VertexPositionTexture[] PenumbraVertices { get; private set; }
|
||||||
public int ShadowVertexCount { get; private set; }
|
public int ShadowVertexCount { get; private set; }
|
||||||
public int PenumbraVertexCount { get; private set; }
|
public int PenumbraVertexCount { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the maximum distance a LOS vertex can be moved to make it align with a nearby LOS segment
|
||||||
|
/// </summary>
|
||||||
|
public float? MaxMergeLosVerticesDist;
|
||||||
|
|
||||||
private readonly HashSet<ConvexHull> overlappingHulls = new HashSet<ConvexHull>();
|
private readonly HashSet<ConvexHull> overlappingHulls = new HashSet<ConvexHull>();
|
||||||
|
|
||||||
public MapEntity ParentEntity { get; private set; }
|
public MapEntity ParentEntity { get; private set; }
|
||||||
@@ -130,61 +133,59 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
public Rectangle BoundingBox { get; private set; }
|
public Rectangle BoundingBox { get; private set; }
|
||||||
|
|
||||||
public ConvexHull(Vector2[] points, Color color, MapEntity parent)
|
public ConvexHull(Rectangle rect, bool isHorizontal, MapEntity parent)
|
||||||
{
|
{
|
||||||
if (shadowEffect == null)
|
shadowEffect ??= new BasicEffect(GameMain.Instance.GraphicsDevice)
|
||||||
{
|
|
||||||
shadowEffect = new BasicEffect(GameMain.Instance.GraphicsDevice)
|
|
||||||
{
|
{
|
||||||
VertexColorEnabled = true
|
VertexColorEnabled = true
|
||||||
};
|
};
|
||||||
}
|
penumbraEffect ??= new BasicEffect(GameMain.Instance.GraphicsDevice)
|
||||||
if (penumbraEffect == null)
|
|
||||||
{
|
|
||||||
penumbraEffect = new BasicEffect(GameMain.Instance.GraphicsDevice)
|
|
||||||
{
|
{
|
||||||
TextureEnabled = true,
|
TextureEnabled = true,
|
||||||
LightingEnabled = false,
|
LightingEnabled = false,
|
||||||
Texture = TextureLoader.FromFile("Content/Lights/penumbra.png")
|
Texture = TextureLoader.FromFile("Content/Lights/penumbra.png")
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
ParentEntity = parent;
|
ParentEntity = parent;
|
||||||
|
|
||||||
ShadowVertices = new VertexPositionColor[6 * 4];
|
ShadowVertices = new VertexPositionColor[6 * 4];
|
||||||
PenumbraVertices = new VertexPositionTexture[6 * 4];
|
PenumbraVertices = new VertexPositionTexture[6 * 4];
|
||||||
|
|
||||||
backFacing = new bool[4];
|
BoundingBox = rect;
|
||||||
ignoreEdge = new bool[4];
|
|
||||||
|
|
||||||
float minX = points[0].X, minY = points[0].Y, maxX = points[0].X, maxY = points[0].Y;
|
this.isHorizontal = isHorizontal;
|
||||||
|
|
||||||
for (int i = 1; i < vertices.Length; i++)
|
|
||||||
{
|
|
||||||
if (points[i].X < minX) minX = points[i].X;
|
|
||||||
if (points[i].Y < minY) minY = points[i].Y;
|
|
||||||
|
|
||||||
if (points[i].X > maxX) maxX = points[i].X;
|
|
||||||
if (points[i].Y > minY) maxY = points[i].Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
BoundingBox = new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
|
|
||||||
|
|
||||||
isHorizontal = BoundingBox.Width > BoundingBox.Height;
|
|
||||||
if (ParentEntity is Structure structure)
|
if (ParentEntity is Structure structure)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.Assert(!structure.Removed);
|
Debug.Assert(!structure.Removed);
|
||||||
isHorizontal = structure.IsHorizontal;
|
isHorizontal = structure.IsHorizontal;
|
||||||
}
|
}
|
||||||
else if (ParentEntity is Item item)
|
else if (ParentEntity is Item item)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.Assert(!item.Removed);
|
Debug.Assert(!item.Removed);
|
||||||
var door = item.GetComponent<Door>();
|
var door = item.GetComponent<Door>();
|
||||||
if (door != null) { isHorizontal = door.IsHorizontal; }
|
if (door != null) { isHorizontal = door.IsHorizontal; }
|
||||||
}
|
}
|
||||||
|
|
||||||
SetVertices(points);
|
Vector2[] verts = new Vector2[]
|
||||||
|
{
|
||||||
|
new Vector2(rect.X, rect.Bottom),
|
||||||
|
new Vector2(rect.Right, rect.Bottom),
|
||||||
|
new Vector2(rect.Right, rect.Y),
|
||||||
|
new Vector2(rect.X, rect.Y),
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector2[] losVerts;
|
||||||
|
if (this.isHorizontal)
|
||||||
|
{
|
||||||
|
thickness = rect.Height;
|
||||||
|
losVerts = new Vector2[] { new Vector2(rect.X, rect.Center.Y), new Vector2(rect.Right, rect.Center.Y) };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
thickness = rect.Width;
|
||||||
|
losVerts = new Vector2[] { new Vector2(rect.Center.X, rect.Y), new Vector2(rect.Center.X, rect.Bottom) };
|
||||||
|
}
|
||||||
|
SetVertices(verts, losVerts);
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
|
|
||||||
var chList = HullLists.Find(h => h.Submarine == parent.Submarine);
|
var chList = HullLists.Find(h => h.Submarine == parent.Submarine);
|
||||||
@@ -196,249 +197,123 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
foreach (ConvexHull ch in chList.List)
|
foreach (ConvexHull ch in chList.List)
|
||||||
{
|
{
|
||||||
MergeOverlappingSegments(ch);
|
MergeLosVertices(ch);
|
||||||
ch.MergeOverlappingSegments(this);
|
ch.MergeLosVertices(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
chList.List.Add(this);
|
chList.List.Add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MergeOverlappingSegments(ConvexHull ch)
|
private void MergeLosVertices(ConvexHull ch, bool refreshOtherOverlappingHulls = true)
|
||||||
{
|
{
|
||||||
if (ch == this) { return; }
|
if (ch == this) { return; }
|
||||||
|
|
||||||
if (isHorizontal == ch.isHorizontal)
|
//merge dist in the direction parallel to the segment
|
||||||
|
//(e.g. how far up/down we can stretch a vertical segment)
|
||||||
|
float mergeDistParallel = MathHelper.Clamp(ch.thickness * 0.65f, 16, 512);
|
||||||
|
if (MaxMergeLosVerticesDist.HasValue)
|
||||||
{
|
{
|
||||||
//hide segments that are roughly at the some position as some other segment (e.g. the ends of two adjacent wall pieces)
|
mergeDistParallel = Math.Max(mergeDistParallel, MaxMergeLosVerticesDist.Value);
|
||||||
float mergeDist = 16;
|
|
||||||
float mergeDistSqr = mergeDist * mergeDist;
|
|
||||||
|
|
||||||
Rectangle intersection = Rectangle.Intersect(BoundingBox, ch.BoundingBox);
|
|
||||||
int intersectionArea = intersection.Width * intersection.Height;
|
|
||||||
int bboxArea = BoundingBox.Width * BoundingBox.Height;
|
|
||||||
int otherBboxArea = ch.BoundingBox.Width * ch.BoundingBox.Height;
|
|
||||||
if (Math.Abs(intersectionArea - bboxArea) < mergeDistSqr) { return; }
|
|
||||||
if (Math.Abs(intersectionArea - otherBboxArea) < mergeDistSqr) { return; }
|
|
||||||
|
|
||||||
for (int i = 0; i < segments.Length; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < ch.segments.Length; j++)
|
|
||||||
{
|
|
||||||
if (segments[i].IsHorizontal != ch.segments[j].IsHorizontal) { continue; }
|
|
||||||
if (ignoreEdge[i] || ch.ignoreEdge[j]) { continue; }
|
|
||||||
|
|
||||||
//the segments must be at different sides of the convex hulls to be merged
|
|
||||||
//(e.g. the right edge of a wall piece and the left edge of another one)
|
|
||||||
var segment1Center = (segments[i].Start.Pos + segments[i].End.Pos) / 2.0f;
|
|
||||||
var segment2Center = (ch.segments[j].Start.Pos + ch.segments[j].End.Pos) / 2.0f;
|
|
||||||
if (Vector2.Dot(segment1Center - BoundingBox.Center.ToVector2(), segment2Center - ch.BoundingBox.Center.ToVector2()) > 0) { continue; }
|
|
||||||
|
|
||||||
if (Vector2.DistanceSquared(segments[i].Start.Pos, ch.segments[j].Start.Pos) < mergeDistSqr &&
|
|
||||||
Vector2.DistanceSquared(segments[i].End.Pos, ch.segments[j].End.Pos) < mergeDistSqr)
|
|
||||||
{
|
|
||||||
ignoreEdge[i] = true;
|
|
||||||
ch.ignoreEdge[j] = true;
|
|
||||||
MergeSegments(segments[i], ch.segments[j], true);
|
|
||||||
}
|
|
||||||
else if (Vector2.DistanceSquared(segments[i].Start.Pos, ch.segments[j].End.Pos) < mergeDistSqr &&
|
|
||||||
Vector2.DistanceSquared(segments[i].End.Pos, ch.segments[j].Start.Pos) < mergeDistSqr)
|
|
||||||
{
|
|
||||||
ignoreEdge[i] = true;
|
|
||||||
ch.ignoreEdge[j] = true;
|
|
||||||
MergeSegments(segments[i], ch.segments[j], false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < segments.Length; i++)
|
|
||||||
{
|
|
||||||
if (ignoreEdge[i]) { continue; }
|
|
||||||
if (Vector2.DistanceSquared(segments[i].Start.Pos, segments[i].End.Pos) < 1.0f) { continue; }
|
|
||||||
for (int j = 0; j < ch.segments.Length; j++)
|
|
||||||
{
|
|
||||||
if (ch.ignoreEdge[j]) { continue; }
|
|
||||||
if (Vector2.DistanceSquared(ch.segments[j].Start.Pos, ch.segments[j].End.Pos) < 1.0f) { continue; }
|
|
||||||
if (IsSegmentAInB(segments[i], ch.segments[j]))
|
|
||||||
{
|
|
||||||
ignoreEdge[i] = true;
|
|
||||||
if (Vector2.DistanceSquared(ch.segments[j].Start.Pos, segments[i].Start.Pos) < 4.0f)
|
|
||||||
{
|
|
||||||
ch.ShiftSegmentPoint(j, false, segments[i].End.Pos);
|
|
||||||
}
|
|
||||||
else if (Vector2.DistanceSquared(ch.segments[j].Start.Pos, segments[i].End.Pos) < 4.0f)
|
|
||||||
{
|
|
||||||
ch.ShiftSegmentPoint(j, false, segments[i].Start.Pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Vector2.DistanceSquared(ch.segments[j].End.Pos, segments[i].Start.Pos) < 4.0f)
|
|
||||||
{
|
|
||||||
ch.ShiftSegmentPoint(j, true, segments[i].End.Pos);
|
|
||||||
}
|
|
||||||
else if (Vector2.DistanceSquared(ch.segments[j].End.Pos, segments[i].End.Pos) < 4.0f)
|
|
||||||
{
|
|
||||||
ch.ShiftSegmentPoint(j, true, segments[i].Start.Pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsSegmentAInB(ch.segments[j], segments[i]))
|
|
||||||
{
|
|
||||||
ch.ignoreEdge[j] = true;
|
|
||||||
|
|
||||||
if (Vector2.DistanceSquared(segments[i].Start.Pos, ch.segments[j].Start.Pos) < 4.0f)
|
|
||||||
{
|
|
||||||
ShiftSegmentPoint(i, false, ch.segments[j].End.Pos);
|
|
||||||
}
|
|
||||||
else if (Vector2.DistanceSquared(segments[i].Start.Pos, ch.segments[j].End.Pos) < 4.0f)
|
|
||||||
{
|
|
||||||
ShiftSegmentPoint(i, false, ch.segments[j].Start.Pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Vector2.DistanceSquared(segments[i].End.Pos, ch.segments[j].Start.Pos) < 4.0f)
|
|
||||||
{
|
|
||||||
ShiftSegmentPoint(i, true, ch.segments[j].End.Pos);
|
|
||||||
}
|
|
||||||
else if (Vector2.DistanceSquared(segments[i].End.Pos, ch.segments[j].End.Pos) < 4.0f)
|
|
||||||
{
|
|
||||||
ShiftSegmentPoint(i, true, ch.segments[j].Start.Pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//ignore edges that are inside some other convex hull
|
|
||||||
for (int i = 0; i < vertices.Length; i++)
|
|
||||||
{
|
|
||||||
if (ch.IsPointInside(vertices[i].Pos))
|
|
||||||
{
|
|
||||||
if (ch.IsPointInside(vertices[(i + 1) % vertices.Length].Pos))
|
|
||||||
{
|
|
||||||
ignoreEdge[i] = true;
|
|
||||||
overlappingHulls.Add(ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShiftSegmentPoint(int segmentIndex, bool end, Vector2 newPos)
|
|
||||||
{
|
|
||||||
var segment = segments[segmentIndex];
|
|
||||||
|
|
||||||
losOffsets[segmentIndex] ??= new VectorPair();
|
|
||||||
bool flipped = false;
|
|
||||||
if (Vector2.DistanceSquared(vertices[segmentIndex].Pos, segment.Start.Pos) > Vector2.DistanceSquared(vertices[segmentIndex].Pos, segment.End.Pos))
|
|
||||||
{
|
|
||||||
flipped = true;
|
|
||||||
}
|
|
||||||
if (end == !flipped)
|
|
||||||
{
|
|
||||||
losOffsets[segmentIndex].B = newPos;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
losOffsets[segmentIndex].A = newPos;
|
Rectangle inflatedAABB = ch.BoundingBox;
|
||||||
}
|
inflatedAABB.Inflate(2, 2);
|
||||||
}
|
//if this los segment isn't touching the other's bounding box,
|
||||||
|
//don't extend the segment by more than 50% of it's length
|
||||||
public static bool IsSegmentAInB(Segment a, Segment b)
|
if (!inflatedAABB.Contains(losVertices[0].Pos) &&
|
||||||
{
|
!inflatedAABB.Contains(losVertices[1].Pos))
|
||||||
if (Vector2.DistanceSquared(a.Start.Pos, a.End.Pos) > Vector2.DistanceSquared(b.Start.Pos, b.End.Pos))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 min = new Vector2(Math.Min(b.Start.Pos.X, b.End.Pos.X), Math.Min(b.Start.Pos.Y, b.End.Pos.Y));
|
|
||||||
min.X -= 1.0f; min.Y -= 1.0f;
|
|
||||||
|
|
||||||
if (a.Start.Pos.X < min.X) { return false; }
|
|
||||||
if (a.Start.Pos.Y < min.Y) { return false; }
|
|
||||||
if (a.End.Pos.X < min.X) { return false; }
|
|
||||||
if (a.End.Pos.Y < min.Y) { return false; }
|
|
||||||
|
|
||||||
Vector2 max = new Vector2(Math.Max(b.Start.Pos.X, b.End.Pos.X), Math.Max(b.Start.Pos.Y, b.End.Pos.Y));
|
|
||||||
max.X += 1.0f; max.Y += 1.0f;
|
|
||||||
|
|
||||||
if (a.Start.Pos.X > max.X) { return false; }
|
|
||||||
if (a.Start.Pos.Y > max.Y) { return false; }
|
|
||||||
if (a.End.Pos.X > max.X) { return false; }
|
|
||||||
if (a.End.Pos.Y > max.Y) { return false; }
|
|
||||||
|
|
||||||
float startDist = MathUtils.LineToPointDistanceSquared(b.Start.Pos, b.End.Pos, a.Start.Pos);
|
|
||||||
if (startDist > 1.0f) { return false; }
|
|
||||||
float endDist = MathUtils.LineToPointDistanceSquared(b.Start.Pos, b.End.Pos, a.End.Pos);
|
|
||||||
if (endDist > 1.0f) { return false; }
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPointInside(Vector2 point)
|
|
||||||
{
|
|
||||||
if (!BoundingBox.Contains(point)) { return false; }
|
|
||||||
|
|
||||||
Vector2 center = (vertices[0].Pos + vertices[1].Pos + vertices[2].Pos + vertices[3].Pos) * 0.25f;
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
Vector2 segmentVector = vertices[(i + 1) % 4].Pos - vertices[i].Pos;
|
|
||||||
Vector2 centerToVertex = center - vertices[i].Pos;
|
|
||||||
Vector2 pointToVertex = point - vertices[i].Pos;
|
|
||||||
|
|
||||||
float dotCenter = Vector2.Dot(centerToVertex, segmentVector);
|
|
||||||
float dotPoint = Vector2.Dot(pointToVertex, segmentVector);
|
|
||||||
|
|
||||||
if ((dotCenter > 0f && dotPoint < 0f) || (dotCenter < 0f && dotPoint > 0f)) { return false; }
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MergeSegments(Segment segment1, Segment segment2, bool startPointsMatch)
|
|
||||||
{
|
|
||||||
int startPointIndex = -1, endPointIndex = -1;
|
|
||||||
for (int i = 0; i < vertices.Length; i++)
|
|
||||||
{
|
|
||||||
if (vertices[i].Pos.NearlyEquals(segment1.Start.Pos))
|
|
||||||
startPointIndex = i;
|
|
||||||
else if (vertices[i].Pos.NearlyEquals(segment1.End.Pos))
|
|
||||||
endPointIndex = i;
|
|
||||||
}
|
|
||||||
if (startPointIndex == -1 || endPointIndex == -1) { return; }
|
|
||||||
|
|
||||||
int startPoint2Index = -1, endPoint2Index = -1;
|
|
||||||
for (int i = 0; i < segment2.ConvexHull.vertices.Length; i++)
|
|
||||||
{
|
|
||||||
if (segment2.ConvexHull.vertices[i].Pos.NearlyEquals(segment2.Start.Pos))
|
|
||||||
startPoint2Index = i;
|
|
||||||
else if (segment2.ConvexHull.vertices[i].Pos.NearlyEquals(segment2.End.Pos))
|
|
||||||
endPoint2Index = i;
|
|
||||||
}
|
|
||||||
if (startPoint2Index == -1 || endPoint2Index == -1) { return; }
|
|
||||||
|
|
||||||
if (startPointsMatch)
|
|
||||||
{
|
|
||||||
losVertices[startPointIndex].Pos = segment2.ConvexHull.losVertices[startPoint2Index].Pos =
|
|
||||||
(segment1.Start.Pos + segment2.Start.Pos) / 2.0f;
|
|
||||||
losVertices[endPointIndex].Pos = segment2.ConvexHull.losVertices[endPoint2Index].Pos =
|
|
||||||
(segment1.End.Pos + segment2.End.Pos) / 2.0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Vector2.DistanceSquared(losVertices[startPointIndex].Pos, segment1.Start.Pos) <
|
|
||||||
Vector2.DistanceSquared(losVertices[startPointIndex].Pos, segment1.End.Pos))
|
|
||||||
{
|
{
|
||||||
losVertices[startPointIndex].Pos = segment2.ConvexHull.losVertices[startPoint2Index].Pos =
|
mergeDistParallel = Math.Min(mergeDistParallel, Vector2.Distance(losVertices[0].Pos, losVertices[1].Pos) * 0.5f);
|
||||||
(segment1.Start.Pos + segment2.End.Pos) / 2.0f;
|
|
||||||
losVertices[endPointIndex].Pos = segment2.ConvexHull.losVertices[endPoint2Index].Pos =
|
|
||||||
(segment1.End.Pos + segment2.Start.Pos) / 2.0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
losVertices[startPointIndex].Pos = segment2.ConvexHull.losVertices[startPoint2Index].Pos =
|
|
||||||
(segment1.End.Pos + segment2.Start.Pos) / 2.0f;
|
|
||||||
losVertices[endPointIndex].Pos = segment2.ConvexHull.losVertices[endPoint2Index].Pos =
|
|
||||||
(segment1.Start.Pos + segment2.End.Pos) / 2.0f;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//merge dist in the direction perpendicular to the segment
|
||||||
|
//(e.g. how far right/left we can stretch a vertical segment)
|
||||||
|
//do not allow more than ~half of the thickness, because that'd make the segment go outside the convex hull
|
||||||
|
float mergeDistPerpendicular = Math.Min(mergeDistParallel, thickness * 0.35f);
|
||||||
|
|
||||||
overlappingHulls.Add(segment2.ConvexHull);
|
Vector2 center = (losVertices[0].Pos + losVertices[1].Pos) / 2;
|
||||||
segment2.ConvexHull.overlappingHulls.Add(this);
|
|
||||||
|
bool changed = false;
|
||||||
|
for (int i = 0; i < losVertices.Length; i++)
|
||||||
|
{
|
||||||
|
Vector2 segmentDir = Vector2.Normalize(losVertices[i].Pos - center);
|
||||||
|
//check if the closest point on the other convex hull segment is close enough, disregarding any offsets
|
||||||
|
//otherwise we might end up moving the vertex too much if we stretch it to an already-offset segment
|
||||||
|
if (!isCloseEnough(
|
||||||
|
MathUtils.GetClosestPointOnLineSegment(ch.losVertices[0].Pos, ch.losVertices[1].Pos, losVertices[i].Pos),
|
||||||
|
losVertices[i].Pos))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check the offset position of the segment next
|
||||||
|
Vector2 closest = MathUtils.GetClosestPointOnLineSegment(
|
||||||
|
ch.losVertices[0].Pos + ch.losOffsets[0],
|
||||||
|
ch.losVertices[1].Pos + ch.losOffsets[1],
|
||||||
|
losVertices[i].Pos);
|
||||||
|
if (!isCloseEnough(closest, losVertices[i].Pos)) { continue; }
|
||||||
|
|
||||||
|
//find where the segments would intersect if they had infinite length
|
||||||
|
// if it's close to the closest point, let's use that instead to keep
|
||||||
|
// the direction of the segment unchanged (i.e. vertical segment stays vertical)
|
||||||
|
if (MathUtils.GetLineIntersection(
|
||||||
|
ch.losVertices[0].Pos + ch.losOffsets[0], ch.losVertices[1].Pos + ch.losOffsets[1],
|
||||||
|
losVertices[0].Pos, losVertices[1].Pos,
|
||||||
|
areLinesInfinite: true, out Vector2 intersection) &&
|
||||||
|
//the intersection needs to be outwards from the vertex we're checking
|
||||||
|
Vector2.Dot(segmentDir, intersection - losVertices[i].Pos) > 0 &&
|
||||||
|
//the intersection needs to be close enough to the default position of the vertex and the closest point
|
||||||
|
//(we don't want to merge the segments somewhere close to infinity!)
|
||||||
|
(Vector2.DistanceSquared(intersection, losVertices[i].Pos) < mergeDistParallel * mergeDistParallel ||
|
||||||
|
Vector2.DistanceSquared(intersection, closest) < 16.0f * 16.0f))
|
||||||
|
{
|
||||||
|
closest = intersection;
|
||||||
|
}
|
||||||
|
|
||||||
|
//don't move the vertices of the segment too close to each other
|
||||||
|
if (Vector2.DistanceSquared(losVertices[1 - i].Pos + losOffsets[1 - i], closest) < mergeDistPerpendicular * mergeDistPerpendicular)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
losOffsets[i] = closest - losVertices[i].Pos;
|
||||||
|
overlappingHulls.Add(ch);
|
||||||
|
ch.overlappingHulls.Add(this);
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
bool isCloseEnough(Vector2 closest, Vector2 vertex)
|
||||||
|
{
|
||||||
|
float dist = Vector2.Distance(closest, vertex);
|
||||||
|
if (dist < 0.001f) { return true; }
|
||||||
|
if (dist > mergeDistParallel) { return false; }
|
||||||
|
|
||||||
|
Vector2 closestDir = (closest - vertex) / dist;
|
||||||
|
|
||||||
|
float dot = Math.Abs(Vector2.Dot(segmentDir, closestDir));
|
||||||
|
float distAlongAxis = dist * dot;
|
||||||
|
if (distAlongAxis > mergeDistParallel) { return false; }
|
||||||
|
|
||||||
|
float distPerpendicular = dist * (1.0f - dot);
|
||||||
|
if (distPerpendicular > mergeDistPerpendicular) { return false; }
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed && refreshOtherOverlappingHulls)
|
||||||
|
{
|
||||||
|
foreach (var overlapping in overlappingHulls)
|
||||||
|
{
|
||||||
|
overlapping.MergeLosVertices(this, refreshOtherOverlappingHulls: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LosIntersects(Vector2 pos1, Vector2 pos2)
|
||||||
|
{
|
||||||
|
return MathUtils.LineSegmentsIntersect(
|
||||||
|
losVertices[0].Pos + losOffsets[0], losVertices[1].Pos + losOffsets[1],
|
||||||
|
pos1, pos2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Rotate(Vector2 origin, float amount)
|
public void Rotate(Vector2 origin, float amount)
|
||||||
@@ -447,7 +322,7 @@ namespace Barotrauma.Lights
|
|||||||
Matrix.CreateTranslation(-origin.X, -origin.Y, 0.0f) *
|
Matrix.CreateTranslation(-origin.X, -origin.Y, 0.0f) *
|
||||||
Matrix.CreateRotationZ(amount) *
|
Matrix.CreateRotationZ(amount) *
|
||||||
Matrix.CreateTranslation(origin.X, origin.Y, 0.0f);
|
Matrix.CreateTranslation(origin.X, origin.Y, 0.0f);
|
||||||
SetVertices(vertices.Select(v => v.Pos).ToArray(), rotationMatrix: rotationMatrix);
|
SetVertices(vertices.Select(v => v.Pos).ToArray(), losVertices.Select(v => v.Pos).ToArray(), rotationMatrix: rotationMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CalculateDimensions()
|
private void CalculateDimensions()
|
||||||
@@ -456,11 +331,10 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
for (int i = 1; i < vertices.Length; i++)
|
for (int i = 1; i < vertices.Length; i++)
|
||||||
{
|
{
|
||||||
if (vertices[i].Pos.X < minX) minX = vertices[i].Pos.X;
|
minX = Math.Min(minX, vertices[i].Pos.X);
|
||||||
if (vertices[i].Pos.Y < minY) minY = vertices[i].Pos.Y;
|
minY = Math.Min(minY, vertices[i].Pos.Y);
|
||||||
|
maxX = Math.Max(maxX, vertices[i].Pos.X);
|
||||||
if (vertices[i].Pos.X > maxX) maxX = vertices[i].Pos.X;
|
maxY = Math.Max(maxY, vertices[i].Pos.Y);
|
||||||
if (vertices[i].Pos.Y > minY) maxY = vertices[i].Pos.Y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundingBox = new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
|
BoundingBox = new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
|
||||||
@@ -471,21 +345,17 @@ namespace Barotrauma.Lights
|
|||||||
for (int i = 0; i < vertices.Length; i++)
|
for (int i = 0; i < vertices.Length; i++)
|
||||||
{
|
{
|
||||||
vertices[i].Pos += amount;
|
vertices[i].Pos += amount;
|
||||||
losVertices[i].Pos += amount;
|
|
||||||
|
|
||||||
losOffsets[i] = null;
|
|
||||||
|
|
||||||
segments[i].Start.Pos += amount;
|
segments[i].Start.Pos += amount;
|
||||||
segments[i].End.Pos += amount;
|
segments[i].End.Pos += amount;
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < losVertices.Length; i++)
|
||||||
|
{
|
||||||
|
losVertices[i].Pos += amount;
|
||||||
|
}
|
||||||
|
|
||||||
LastVertexChangeTime = (float)Timing.TotalTime;
|
LastVertexChangeTime = (float)Timing.TotalTime;
|
||||||
|
|
||||||
overlappingHulls.Clear();
|
overlappingHulls.Clear();
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
ignoreEdge[i] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CalculateDimensions();
|
CalculateDimensions();
|
||||||
|
|
||||||
@@ -497,8 +367,8 @@ namespace Barotrauma.Lights
|
|||||||
overlappingHulls.Clear();
|
overlappingHulls.Clear();
|
||||||
foreach (ConvexHull ch in chList.List)
|
foreach (ConvexHull ch in chList.List)
|
||||||
{
|
{
|
||||||
MergeOverlappingSegments(ch);
|
MergeLosVertices(ch);
|
||||||
ch.MergeOverlappingSegments(this);
|
ch.MergeLosVertices(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -511,23 +381,23 @@ namespace Barotrauma.Lights
|
|||||||
foreach (ConvexHull ch in chList.List)
|
foreach (ConvexHull ch in chList.List)
|
||||||
{
|
{
|
||||||
ch.overlappingHulls.Clear();
|
ch.overlappingHulls.Clear();
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < ch.losOffsets.Length; i++)
|
||||||
{
|
{
|
||||||
ch.ignoreEdge[i] = false;
|
ch.losOffsets[i] = Vector2.Zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < chList.List.Count; i++)
|
for (int i = 0; i < chList.List.Count; i++)
|
||||||
{
|
{
|
||||||
for (int j = i + 1; j < chList.List.Count; j++)
|
for (int j = i + 1; j < chList.List.Count; j++)
|
||||||
{
|
{
|
||||||
chList.List[i].MergeOverlappingSegments(chList.List[j]);
|
chList.List[i].MergeLosVertices(chList.List[j]);
|
||||||
chList.List[j].MergeOverlappingSegments(chList.List[i]);
|
chList.List[j].MergeLosVertices(chList.List[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetVertices(Vector2[] points, bool mergeOverlappingSegments = true, Matrix? rotationMatrix = null)
|
public void SetVertices(Vector2[] points, Vector2[] losPoints, bool mergeOverlappingSegments = true, Matrix? rotationMatrix = null)
|
||||||
{
|
{
|
||||||
Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported");
|
Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported");
|
||||||
|
|
||||||
@@ -535,39 +405,24 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
vertices[i] = new SegmentPoint(points[i], this);
|
vertices[i] = new SegmentPoint(points[i], this);
|
||||||
losVertices[i] = new SegmentPoint(points[i], this);
|
|
||||||
losOffsets[i] = null;
|
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
{
|
||||||
ignoreEdge[i] = false;
|
losVertices[i] = new SegmentPoint(losPoints[i], this);
|
||||||
|
losOffsets[i] = Vector2.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
overlappingHulls.Clear();
|
overlappingHulls.Clear();
|
||||||
|
|
||||||
int margin = 0;
|
|
||||||
if (Math.Abs(points[0].X - points[2].X) < Math.Abs(points[0].Y - points[2].Y))
|
|
||||||
{
|
|
||||||
losVertices[0].Pos = new Vector2(points[0].X + margin, points[0].Y);
|
|
||||||
losVertices[1].Pos = new Vector2(points[1].X + margin, points[1].Y);
|
|
||||||
losVertices[2].Pos = new Vector2(points[2].X - margin, points[2].Y);
|
|
||||||
losVertices[3].Pos = new Vector2(points[3].X - margin, points[3].Y);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
losVertices[0].Pos = new Vector2(points[0].X, points[0].Y + margin);
|
|
||||||
losVertices[1].Pos = new Vector2(points[1].X, points[1].Y - margin);
|
|
||||||
losVertices[2].Pos = new Vector2(points[2].X, points[2].Y - margin);
|
|
||||||
losVertices[3].Pos = new Vector2(points[3].X, points[3].Y + margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rotationMatrix.HasValue)
|
if (rotationMatrix.HasValue)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < vertices.Length; i++)
|
for (int i = 0; i < vertices.Length; i++)
|
||||||
{
|
{
|
||||||
vertices[i].Pos = Vector2.Transform(vertices[i].Pos, rotationMatrix.Value);
|
vertices[i].Pos = Vector2.Transform(vertices[i].Pos, rotationMatrix.Value);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < losVertices.Length; i++)
|
||||||
|
{
|
||||||
losVertices[i].Pos = Vector2.Transform(losVertices[i].Pos, rotationMatrix.Value);
|
losVertices[i].Pos = Vector2.Transform(losVertices[i].Pos, rotationMatrix.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -588,7 +443,7 @@ namespace Barotrauma.Lights
|
|||||||
overlappingHulls.Clear();
|
overlappingHulls.Clear();
|
||||||
foreach (ConvexHull ch in chList.List)
|
foreach (ConvexHull ch in chList.List)
|
||||||
{
|
{
|
||||||
MergeOverlappingSegments(ch);
|
MergeLosVertices(ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -610,31 +465,17 @@ namespace Barotrauma.Lights
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the segments that are facing towards viewPosition
|
/// Returns the segments that are facing towards viewPosition
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void GetVisibleSegments(Vector2 viewPosition, List<Segment> visibleSegments, bool ignoreEdges)
|
public void GetVisibleSegments(Vector2 viewPosition, List<Segment> visibleSegments)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
if (ignoreEdge[i] && ignoreEdges) { continue; }
|
if (IsSegmentFacing(vertices[i].WorldPos, vertices[(i + 1) % 4].WorldPos, viewPosition))
|
||||||
|
|
||||||
Vector2 pos1 = vertices[i].WorldPos;
|
|
||||||
Vector2 pos2 = vertices[(i + 1) % 4].WorldPos;
|
|
||||||
|
|
||||||
Vector2 middle = (pos1 + pos2) / 2;
|
|
||||||
|
|
||||||
Vector2 L = viewPosition - middle;
|
|
||||||
|
|
||||||
Vector2 N = new Vector2(
|
|
||||||
-(pos2.Y - pos1.Y),
|
|
||||||
pos2.X - pos1.X);
|
|
||||||
|
|
||||||
if (Vector2.Dot(N, L) > 0)
|
|
||||||
{
|
{
|
||||||
visibleSegments.Add(segments[i]);
|
visibleSegments.Add(segments[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void RefreshWorldPositions()
|
public void RefreshWorldPositions()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
@@ -662,34 +503,12 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
ShadowVertexCount = 0;
|
ShadowVertexCount = 0;
|
||||||
|
|
||||||
//compute facing of each edge, using N*L
|
for (int i = 0; i < losVertices.Length; i++)
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
{
|
||||||
if (ignoreEdge[i])
|
|
||||||
{
|
|
||||||
backFacing[i] = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 firstVertex = losVertices[i].Pos;
|
|
||||||
Vector2 secondVertex = losVertices[(i+1) % 4].Pos;
|
|
||||||
|
|
||||||
Vector2 L = lightSourcePos - ((firstVertex + secondVertex) / 2.0f);
|
|
||||||
|
|
||||||
Vector2 N = new Vector2(
|
|
||||||
-(secondVertex.Y - firstVertex.Y),
|
|
||||||
secondVertex.X - firstVertex.X);
|
|
||||||
|
|
||||||
backFacing[i] = (Vector2.Dot(N, L) < 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ShadowVertexCount = 0;
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
if (!backFacing[i]) { continue; }
|
|
||||||
int currentIndex = i;
|
int currentIndex = i;
|
||||||
Vector3 vertexPos0 = new Vector3(losOffsets[currentIndex]?.A ?? losVertices[currentIndex].Pos, 0.0f);
|
int nextIndex = (currentIndex + 1) % 2;
|
||||||
Vector3 vertexPos1 = new Vector3(losOffsets[currentIndex]?.B ?? losVertices[(currentIndex + 1) % 4].Pos, 0.0f);
|
Vector3 vertexPos0 = new Vector3(losVertices[currentIndex].Pos + losOffsets[currentIndex], 0.0f);
|
||||||
|
Vector3 vertexPos1 = new Vector3(losVertices[nextIndex].Pos + losOffsets[nextIndex], 0.0f);
|
||||||
|
|
||||||
if (Vector3.DistanceSquared(vertexPos0, vertexPos1) < 1.0f) { continue; }
|
if (Vector3.DistanceSquared(vertexPos0, vertexPos1) < 1.0f) { continue; }
|
||||||
|
|
||||||
@@ -740,9 +559,24 @@ namespace Barotrauma.Lights
|
|||||||
ShadowVertexCount += 6;
|
ShadowVertexCount += 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsSegmentFacing(losVertices[0].Pos, losVertices[1].Pos, lightSourcePos))
|
||||||
|
{
|
||||||
|
Array.Reverse(ShadowVertices);
|
||||||
|
}
|
||||||
|
|
||||||
CalculateLosPenumbraVertices(lightSourcePos);
|
CalculateLosPenumbraVertices(lightSourcePos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsSegmentFacing(Vector2 segmentPos1, Vector2 segmentPos2, Vector2 viewPosition)
|
||||||
|
{
|
||||||
|
Vector2 segmentMid = (segmentPos1 + segmentPos2) / 2;
|
||||||
|
Vector2 segmentDiff = segmentPos2 - segmentPos1;
|
||||||
|
Vector2 segmentNormal = new Vector2(-segmentDiff.Y, segmentDiff.X);
|
||||||
|
|
||||||
|
Vector2 viewDirection = viewPosition - segmentMid;
|
||||||
|
return Vector2.Dot(segmentNormal, viewDirection) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
private void CalculateLosPenumbraVertices(Vector2 lightSourcePos)
|
private void CalculateLosPenumbraVertices(Vector2 lightSourcePos)
|
||||||
{
|
{
|
||||||
Vector3 offset = Vector3.Zero;
|
Vector3 offset = Vector3.Zero;
|
||||||
@@ -752,73 +586,104 @@ namespace Barotrauma.Lights
|
|||||||
}
|
}
|
||||||
|
|
||||||
PenumbraVertexCount = 0;
|
PenumbraVertexCount = 0;
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < losVertices.Length; i++)
|
||||||
{
|
{
|
||||||
int currentIndex = i;
|
int currentIndex = i;
|
||||||
int prevIndex = (i + 3) % 4;
|
int nextIndex = (i + 1) % 2;
|
||||||
int nextIndex = (i + 1) % 4;
|
Vector2 vertexPos0 = losVertices[currentIndex].Pos + losOffsets[currentIndex];
|
||||||
bool disjointed = losOffsets[i]?.A != null;
|
Vector2 vertexPos1 = losVertices[nextIndex].Pos + losOffsets[nextIndex];
|
||||||
Vector2 vertexPos0 = losOffsets[currentIndex]?.A ?? losVertices[currentIndex].Pos;
|
|
||||||
Vector2 vertexPos1 = losOffsets[currentIndex]?.B ?? losVertices[nextIndex].Pos;
|
|
||||||
|
|
||||||
if (Vector2.DistanceSquared(vertexPos0, vertexPos1) < 1.0f) { continue; }
|
if (Vector2.DistanceSquared(vertexPos0, vertexPos1) < 1.0f) { continue; }
|
||||||
|
|
||||||
if (backFacing[currentIndex] && (disjointed || (!backFacing[prevIndex])))
|
Vector3 penumbraStart = new Vector3(vertexPos0, 0.0f);
|
||||||
|
|
||||||
|
PenumbraVertices[PenumbraVertexCount] = new VertexPositionTexture
|
||||||
{
|
{
|
||||||
Vector3 penumbraStart = new Vector3(vertexPos0, 0.0f);
|
Position = penumbraStart + offset,
|
||||||
|
TextureCoordinate = new Vector2(0.0f, 1.0f)
|
||||||
|
};
|
||||||
|
|
||||||
PenumbraVertices[PenumbraVertexCount] = new VertexPositionTexture
|
for (int j = 0; j < 2; j++)
|
||||||
{
|
{
|
||||||
Position = penumbraStart + offset,
|
PenumbraVertices[PenumbraVertexCount + j + 1] = new VertexPositionTexture();
|
||||||
TextureCoordinate = new Vector2(0.0f, 1.0f)
|
Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
|
||||||
};
|
vertexDir.Normalize();
|
||||||
|
|
||||||
for (int j = 0; j < 2; j++)
|
Vector3 normal = (j == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
|
||||||
{
|
|
||||||
PenumbraVertices[PenumbraVertexCount + j + 1] = new VertexPositionTexture();
|
|
||||||
Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
|
|
||||||
vertexDir.Normalize();
|
|
||||||
|
|
||||||
Vector3 normal = (j == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
|
vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) - normal * 20.0f);
|
||||||
|
vertexDir.Normalize();
|
||||||
|
PenumbraVertices[PenumbraVertexCount + j + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000 + offset;
|
||||||
|
|
||||||
vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) - normal * 20.0f);
|
PenumbraVertices[PenumbraVertexCount + j + 1].TextureCoordinate = (j == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
|
||||||
vertexDir.Normalize();
|
|
||||||
PenumbraVertices[PenumbraVertexCount + j + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000 + offset;
|
|
||||||
|
|
||||||
PenumbraVertices[PenumbraVertexCount + j + 1].TextureCoordinate = (j == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
PenumbraVertexCount += 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disjointed = losOffsets[i]?.B != null;
|
PenumbraVertexCount += 3;
|
||||||
if (backFacing[currentIndex] && (disjointed || (!backFacing[nextIndex])))
|
|
||||||
|
penumbraStart = new Vector3(vertexPos1, 0.0f);
|
||||||
|
|
||||||
|
PenumbraVertices[PenumbraVertexCount] = new VertexPositionTexture
|
||||||
{
|
{
|
||||||
Vector3 penumbraStart = new Vector3(vertexPos1, 0.0f);
|
Position = penumbraStart + offset,
|
||||||
|
TextureCoordinate = new Vector2(0.0f, 1.0f)
|
||||||
|
};
|
||||||
|
|
||||||
PenumbraVertices[PenumbraVertexCount] = new VertexPositionTexture
|
for (int j = 0; j < 2; j++)
|
||||||
{
|
{
|
||||||
Position = penumbraStart + offset,
|
PenumbraVertices[PenumbraVertexCount + (1 - j) + 1] = new VertexPositionTexture();
|
||||||
TextureCoordinate = new Vector2(0.0f, 1.0f)
|
Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
|
||||||
};
|
vertexDir.Normalize();
|
||||||
|
|
||||||
for (int j = 0; j < 2; j++)
|
Vector3 normal = (j == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
|
||||||
{
|
|
||||||
PenumbraVertices[PenumbraVertexCount + (1 - j) + 1] = new VertexPositionTexture();
|
|
||||||
Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
|
|
||||||
vertexDir.Normalize();
|
|
||||||
|
|
||||||
Vector3 normal = (j == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
|
vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) + normal * 20.0f);
|
||||||
|
vertexDir.Normalize();
|
||||||
|
PenumbraVertices[PenumbraVertexCount + (1 - j) + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000 + offset;
|
||||||
|
|
||||||
vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) + normal * 20.0f);
|
PenumbraVertices[PenumbraVertexCount + (1 - j) + 1].TextureCoordinate = (j == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
|
||||||
vertexDir.Normalize();
|
|
||||||
PenumbraVertices[PenumbraVertexCount + (1 - j) + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000 + offset;
|
|
||||||
|
|
||||||
PenumbraVertices[PenumbraVertexCount + (1 - j) + 1].TextureCoordinate = (j == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
PenumbraVertexCount += 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PenumbraVertexCount += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DebugDraw(SpriteBatch spriteBatch)
|
||||||
|
{
|
||||||
|
//RecalculateAll(Submarine.MainSub);
|
||||||
|
//RefreshWorldPositions();
|
||||||
|
|
||||||
|
DrawLine(losVertices[0].Pos, losVertices[1].Pos, Color.Gray * 0.5f, width: 3);
|
||||||
|
DrawLine(losVertices[0].Pos + losOffsets[0], losVertices[1].Pos + losOffsets[1], Color.LightGreen, width: 2);
|
||||||
|
DrawLine(GameMain.GameScreen.Cam.Position + Vector2.One * 1000, GameMain.GameScreen.Cam.Position - Vector2.One * 1000, Color.Magenta, width: 2);
|
||||||
|
|
||||||
|
if (GameMain.LightManager.LightingEnabled)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < vertices.Length; i++)
|
||||||
|
{
|
||||||
|
Vector2 start = vertices[i].Pos;
|
||||||
|
Vector2 end = vertices[(i + 1) % 4].Pos;
|
||||||
|
DrawLine(
|
||||||
|
start,
|
||||||
|
end, Color.Yellow * 0.5f,
|
||||||
|
width: 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawLine(Vector2 vertexPos0, Vector2 vertexPos1, Color color, int width)
|
||||||
|
{
|
||||||
|
if (ParentEntity != null && ParentEntity.Submarine != null)
|
||||||
|
{
|
||||||
|
vertexPos0 += ParentEntity.Submarine.DrawPosition;
|
||||||
|
vertexPos1 += ParentEntity.Submarine.DrawPosition;
|
||||||
|
}
|
||||||
|
float alpha = 1.0f;
|
||||||
|
if (LightManager.ViewTarget != null)
|
||||||
|
{
|
||||||
|
alpha = IsSegmentFacing(vertexPos0, vertexPos1, LightManager.ViewTarget.WorldPosition) ? 1.0f : 0.5f;
|
||||||
|
}
|
||||||
|
vertexPos0.Y = -vertexPos0.Y;
|
||||||
|
vertexPos1.Y = -vertexPos1.Y;
|
||||||
|
GUI.DrawLine(spriteBatch, vertexPos0, vertexPos1, color * alpha, width: width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -889,16 +754,13 @@ namespace Barotrauma.Lights
|
|||||||
{
|
{
|
||||||
HullLists.Remove(chList);
|
HullLists.Remove(chList);
|
||||||
}
|
}
|
||||||
foreach (ConvexHull ch2 in overlappingHulls)
|
//create a new list because MergeLosVertices can edit overlappingHulls
|
||||||
|
foreach (ConvexHull ch2 in overlappingHulls.ToList())
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
ch2.ignoreEdge[i] = false;
|
|
||||||
}
|
|
||||||
ch2.overlappingHulls.Remove(this);
|
ch2.overlappingHulls.Remove(this);
|
||||||
foreach (ConvexHull ch in chList.List)
|
foreach (ConvexHull ch in chList.List)
|
||||||
{
|
{
|
||||||
ch.MergeOverlappingSegments(ch2);
|
ch.MergeLosVertices(ch2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System;
|
using System;
|
||||||
using Barotrauma.Items.Components;
|
using Barotrauma.Items.Components;
|
||||||
using Barotrauma.Extensions;
|
using Barotrauma.Extensions;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Barotrauma.Lights
|
namespace Barotrauma.Lights
|
||||||
{
|
{
|
||||||
@@ -22,6 +23,9 @@ namespace Barotrauma.Lights
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
const float ObstructLightsBehindCharactersZoomThreshold = 0.5f;
|
const float ObstructLightsBehindCharactersZoomThreshold = 0.5f;
|
||||||
|
|
||||||
|
private Thread rayCastThread;
|
||||||
|
private Queue<RayCastTask> pendingRayCasts = new Queue<RayCastTask>();
|
||||||
|
|
||||||
public static Entity ViewTarget { get; set; }
|
public static Entity ViewTarget { get; set; }
|
||||||
|
|
||||||
private float currLightMapScale;
|
private float currLightMapScale;
|
||||||
@@ -58,6 +62,8 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
private readonly List<LightSource> lights;
|
private readonly List<LightSource> lights;
|
||||||
|
|
||||||
|
public bool DebugLos;
|
||||||
|
|
||||||
public bool LosEnabled = true;
|
public bool LosEnabled = true;
|
||||||
public float LosAlpha = 1f;
|
public float LosAlpha = 1f;
|
||||||
public LosMode LosMode = LosMode.Transparent;
|
public LosMode LosMode = LosMode.Transparent;
|
||||||
@@ -68,6 +74,8 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
private readonly Texture2D visionCircle;
|
private readonly Texture2D visionCircle;
|
||||||
|
|
||||||
|
private readonly Texture2D gapGlowTexture;
|
||||||
|
|
||||||
private Vector2 losOffset;
|
private Vector2 losOffset;
|
||||||
|
|
||||||
private int recalculationCount;
|
private int recalculationCount;
|
||||||
@@ -85,8 +93,16 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
AmbientLight = new Color(20, 20, 20, 255);
|
AmbientLight = new Color(20, 20, 20, 255);
|
||||||
|
|
||||||
|
rayCastThread = new Thread(UpdateRayCasts)
|
||||||
|
{
|
||||||
|
Name = "LightManager Raycast thread",
|
||||||
|
IsBackground = true //this should kill the thread if the game crashes
|
||||||
|
};
|
||||||
|
rayCastThread.Start();
|
||||||
|
|
||||||
visionCircle = Sprite.LoadTexture("Content/Lights/visioncircle.png");
|
visionCircle = Sprite.LoadTexture("Content/Lights/visioncircle.png");
|
||||||
highlightRaster = Sprite.LoadTexture("Content/UI/HighlightRaster.png");
|
highlightRaster = Sprite.LoadTexture("Content/UI/HighlightRaster.png");
|
||||||
|
gapGlowTexture = Sprite.LoadTexture("Content/Lights/pointlight_rays.png");
|
||||||
|
|
||||||
GameMain.Instance.ResolutionChanged += () =>
|
GameMain.Instance.ResolutionChanged += () =>
|
||||||
{
|
{
|
||||||
@@ -100,15 +116,12 @@ namespace Barotrauma.Lights
|
|||||||
LosEffect = EffectLoader.Load("Effects/losshader");
|
LosEffect = EffectLoader.Load("Effects/losshader");
|
||||||
SolidColorEffect = EffectLoader.Load("Effects/solidcolor");
|
SolidColorEffect = EffectLoader.Load("Effects/solidcolor");
|
||||||
|
|
||||||
if (lightEffect == null)
|
lightEffect ??= new BasicEffect(GameMain.Instance.GraphicsDevice)
|
||||||
{
|
|
||||||
lightEffect = new BasicEffect(GameMain.Instance.GraphicsDevice)
|
|
||||||
{
|
{
|
||||||
VertexColorEnabled = true,
|
VertexColorEnabled = true,
|
||||||
TextureEnabled = true,
|
TextureEnabled = true,
|
||||||
Texture = LightSource.LightTexture
|
Texture = LightSource.LightTexture
|
||||||
};
|
};
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,6 +189,51 @@ namespace Barotrauma.Lights
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class RayCastTask
|
||||||
|
{
|
||||||
|
public LightSource LightSource;
|
||||||
|
public Vector2 DrawPos;
|
||||||
|
public float Rotation;
|
||||||
|
|
||||||
|
public RayCastTask(LightSource lightSource, Vector2 drawPos, float rotation)
|
||||||
|
{
|
||||||
|
LightSource = lightSource;
|
||||||
|
DrawPos = drawPos;
|
||||||
|
Rotation = rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Calculate()
|
||||||
|
{
|
||||||
|
LightSource.RayCastTask(DrawPos, Rotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly object mutex = new object();
|
||||||
|
|
||||||
|
public void AddRayCastTask(LightSource lightSource, Vector2 drawPos, float rotation)
|
||||||
|
{
|
||||||
|
lock (mutex)
|
||||||
|
{
|
||||||
|
if (pendingRayCasts.Any(p => p.LightSource == lightSource)) { return; }
|
||||||
|
pendingRayCasts.Enqueue(new RayCastTask(lightSource, drawPos, rotation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRayCasts()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
lock (mutex)
|
||||||
|
{
|
||||||
|
while (pendingRayCasts.Count > 0)
|
||||||
|
{
|
||||||
|
pendingRayCasts.Dequeue().Calculate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Thread.Sleep(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void RenderLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null)
|
public void RenderLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null)
|
||||||
{
|
{
|
||||||
if (!LightingEnabled) { return; }
|
if (!LightingEnabled) { return; }
|
||||||
@@ -288,8 +346,8 @@ namespace Barotrauma.Lights
|
|||||||
foreach (LightSource light in activeLights)
|
foreach (LightSource light in activeLights)
|
||||||
{
|
{
|
||||||
if (!light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
|
if (!light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
|
||||||
light.DrawSprite(spriteBatch, cam);
|
|
||||||
light.DrawLightVolume(spriteBatch, lightEffect, transform, recalculationCount < MaxLightVolumeRecalculationsPerFrame, ref recalculationCount);
|
light.DrawLightVolume(spriteBatch, lightEffect, transform, recalculationCount < MaxLightVolumeRecalculationsPerFrame, ref recalculationCount);
|
||||||
|
light.DrawSprite(spriteBatch, cam);
|
||||||
}
|
}
|
||||||
GameMain.ParticleManager.Draw(spriteBatch, true, null, Particles.ParticleBlendState.Additive);
|
GameMain.ParticleManager.Draw(spriteBatch, true, null, Particles.ParticleBlendState.Additive);
|
||||||
spriteBatch.End();
|
spriteBatch.End();
|
||||||
@@ -308,15 +366,46 @@ namespace Barotrauma.Lights
|
|||||||
}
|
}
|
||||||
spriteBatch.End();
|
spriteBatch.End();
|
||||||
|
|
||||||
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"];
|
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
|
||||||
SolidColorEffect.Parameters["color"].SetValue(AmbientLight.Opaque().ToVector4());
|
Vector3 glowColorHSV = ToolBox.RGBToHSV(AmbientLight);
|
||||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform, effect: SolidColorEffect);
|
glowColorHSV.Z = Math.Max(glowColorHSV.Z, 0.4f);
|
||||||
Submarine.DrawDamageable(spriteBatch, null);
|
Color glowColor = ToolBox.HSVToRGB(glowColorHSV.X, glowColorHSV.Y, glowColorHSV.Z);
|
||||||
|
Vector2 glowSpriteSize = new Vector2(gapGlowTexture.Width, gapGlowTexture.Height);
|
||||||
|
foreach (var gap in Gap.GapList)
|
||||||
|
{
|
||||||
|
if (gap.IsRoomToRoom || gap.Open <= 0.0f || gap.ConnectedWall == null) { continue; }
|
||||||
|
|
||||||
|
float a = MathHelper.Lerp(0.5f, 1.0f,
|
||||||
|
PerlinNoise.GetPerlin((float)Timing.TotalTime * 0.05f, gap.GlowEffectT));
|
||||||
|
|
||||||
|
float scale = MathHelper.Lerp(0.5f, 2.0f,
|
||||||
|
PerlinNoise.GetPerlin((float)Timing.TotalTime * 0.01f, gap.GlowEffectT));
|
||||||
|
|
||||||
|
float rot = PerlinNoise.GetPerlin((float)Timing.TotalTime * 0.001f, gap.GlowEffectT) * MathHelper.TwoPi;
|
||||||
|
|
||||||
|
Vector2 spriteScale = new Vector2(gap.Rect.Width, gap.Rect.Height) / glowSpriteSize;
|
||||||
|
Vector2 drawPos = new Vector2(gap.DrawPosition.X, -gap.DrawPosition.Y);
|
||||||
|
|
||||||
|
spriteBatch.Draw(gapGlowTexture,
|
||||||
|
drawPos,
|
||||||
|
null,
|
||||||
|
glowColor * a,
|
||||||
|
rot,
|
||||||
|
glowSpriteSize / 2,
|
||||||
|
scale: Math.Max(spriteScale.X, spriteScale.Y) * scale,
|
||||||
|
SpriteEffects.None,
|
||||||
|
layerDepth: 0);
|
||||||
|
}
|
||||||
|
spriteBatch.End();
|
||||||
|
|
||||||
|
GameMain.GameScreen.DamageEffect.CurrentTechnique = GameMain.GameScreen.DamageEffect.Techniques["StencilShaderSolidColor"];
|
||||||
|
GameMain.GameScreen.DamageEffect.Parameters["solidColor"].SetValue(Color.Black.ToVector4());
|
||||||
|
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.LinearWrap, transformMatrix: spriteBatchTransform, effect: GameMain.GameScreen.DamageEffect);
|
||||||
|
Submarine.DrawDamageable(spriteBatch, GameMain.GameScreen.DamageEffect);
|
||||||
spriteBatch.End();
|
spriteBatch.End();
|
||||||
|
|
||||||
graphics.BlendState = BlendState.Additive;
|
graphics.BlendState = BlendState.Additive;
|
||||||
|
|
||||||
|
|
||||||
//draw the focused item and character to highlight them,
|
//draw the focused item and character to highlight them,
|
||||||
//and light sprites (done before drawing the actual light volumes so we can make characters obstruct the highlights and sprites)
|
//and light sprites (done before drawing the actual light volumes so we can make characters obstruct the highlights and sprites)
|
||||||
//---------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------
|
||||||
@@ -389,6 +478,17 @@ namespace Barotrauma.Lights
|
|||||||
light.DrawLightVolume(spriteBatch, lightEffect, transform, recalculationCount < MaxLightVolumeRecalculationsPerFrame, ref recalculationCount);
|
light.DrawLightVolume(spriteBatch, lightEffect, transform, recalculationCount < MaxLightVolumeRecalculationsPerFrame, ref recalculationCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ConnectionPanel.ShouldDebugDrawWiring)
|
||||||
|
{
|
||||||
|
foreach (MapEntity e in (Submarine.VisibleEntities ?? MapEntity.mapEntityList))
|
||||||
|
{
|
||||||
|
if (e is Item item && item.GetComponent<Wire>() is Wire wire)
|
||||||
|
{
|
||||||
|
wire.DebugDraw(spriteBatch, alpha: 0.4f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lightEffect.World = transform;
|
lightEffect.World = transform;
|
||||||
|
|
||||||
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
|
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
|
||||||
@@ -566,7 +666,7 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
public void UpdateObstructVision(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Vector2 lookAtPosition)
|
public void UpdateObstructVision(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Vector2 lookAtPosition)
|
||||||
{
|
{
|
||||||
if ((!LosEnabled || LosMode == LosMode.None) && !ObstructVision) return;
|
if ((!LosEnabled || LosMode == LosMode.None) && !ObstructVision) { return; }
|
||||||
if (ViewTarget == null) return;
|
if (ViewTarget == null) return;
|
||||||
|
|
||||||
graphics.SetRenderTarget(LosTexture);
|
graphics.SetRenderTarget(LosTexture);
|
||||||
@@ -598,23 +698,52 @@ namespace Barotrauma.Lights
|
|||||||
if (LosEnabled && LosMode != LosMode.None && ViewTarget != null)
|
if (LosEnabled && LosMode != LosMode.None && ViewTarget != null)
|
||||||
{
|
{
|
||||||
Vector2 pos = ViewTarget.DrawPosition;
|
Vector2 pos = ViewTarget.DrawPosition;
|
||||||
|
bool centeredOnHead = false;
|
||||||
|
if (ViewTarget is Character character &&
|
||||||
|
character.AnimController?.GetLimb(LimbType.Head) is Limb head &&
|
||||||
|
!head.IsSevered && !head.Removed)
|
||||||
|
{
|
||||||
|
pos = head.body.DrawPosition;
|
||||||
|
centeredOnHead = true;
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle camView = new Rectangle(cam.WorldView.X, cam.WorldView.Y - cam.WorldView.Height, cam.WorldView.Width, cam.WorldView.Height);
|
Rectangle camView = new Rectangle(cam.WorldView.X, cam.WorldView.Y - cam.WorldView.Height, cam.WorldView.Width, cam.WorldView.Height);
|
||||||
|
|
||||||
Matrix shadowTransform = cam.ShaderTransform
|
Matrix shadowTransform = cam.ShaderTransform
|
||||||
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
|
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
|
||||||
|
|
||||||
var convexHulls = ConvexHull.GetHullsInRange(ViewTarget.Position, cam.WorldView.Width*0.75f, ViewTarget.Submarine);
|
var convexHulls = ConvexHull.GetHullsInRange(ViewTarget.Position, cam.WorldView.Width * 0.75f, ViewTarget.Submarine);
|
||||||
|
|
||||||
|
//make sure the head isn't peeking through any LOS segments, and if it is,
|
||||||
|
//center the LOS on the character's collider instead
|
||||||
|
if (centeredOnHead)
|
||||||
|
{
|
||||||
|
foreach (var ch in convexHulls)
|
||||||
|
{
|
||||||
|
Vector2 currentViewPos = pos;
|
||||||
|
Vector2 defaultViewPos = ViewTarget.DrawPosition;
|
||||||
|
if (ch.ParentEntity?.Submarine != null)
|
||||||
|
{
|
||||||
|
defaultViewPos -= ch.ParentEntity.Submarine.DrawPosition;
|
||||||
|
currentViewPos -= ch.ParentEntity.Submarine.DrawPosition;
|
||||||
|
}
|
||||||
|
//check if a line from the character's collider to the head intersects with the los segment (= head poking through it)
|
||||||
|
if (ch.LosIntersects(defaultViewPos, currentViewPos))
|
||||||
|
{
|
||||||
|
pos = ViewTarget.DrawPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (convexHulls != null)
|
if (convexHulls != null)
|
||||||
{
|
{
|
||||||
List<VertexPositionColor> shadowVerts = new List<VertexPositionColor>();
|
List<VertexPositionColor> shadowVerts = new List<VertexPositionColor>();
|
||||||
List<VertexPositionTexture> penumbraVerts = new List<VertexPositionTexture>();
|
List<VertexPositionTexture> penumbraVerts = new List<VertexPositionTexture>();
|
||||||
foreach (ConvexHull convexHull in convexHulls)
|
foreach (ConvexHull convexHull in convexHulls)
|
||||||
{
|
{
|
||||||
if (!convexHull.Enabled || !convexHull.Intersects(camView)) continue;
|
if (!convexHull.Enabled || !convexHull.Intersects(camView)) { continue; }
|
||||||
|
|
||||||
Vector2 relativeLightPos = pos;
|
Vector2 relativeLightPos = pos;
|
||||||
if (convexHull.ParentEntity?.Submarine != null) relativeLightPos -= convexHull.ParentEntity.Submarine.Position;
|
if (convexHull.ParentEntity?.Submarine != null) { relativeLightPos -= convexHull.ParentEntity.Submarine.Position; }
|
||||||
|
|
||||||
convexHull.CalculateLosVertices(relativeLightPos);
|
convexHull.CalculateLosVertices(relativeLightPos);
|
||||||
|
|
||||||
@@ -647,6 +776,21 @@ namespace Barotrauma.Lights
|
|||||||
graphics.SetRenderTarget(null);
|
graphics.SetRenderTarget(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DebugDrawLos(SpriteBatch spriteBatch, Camera cam)
|
||||||
|
{
|
||||||
|
Vector2 pos = ViewTarget?.Position ?? cam.Position;
|
||||||
|
spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cam.Transform);
|
||||||
|
var convexHulls = ConvexHull.GetHullsInRange(pos, cam.WorldView.Width * 0.75f, ViewTarget?.Submarine);
|
||||||
|
Rectangle camView = new Rectangle(cam.WorldView.X, cam.WorldView.Y - cam.WorldView.Height, cam.WorldView.Width, cam.WorldView.Height);
|
||||||
|
foreach (ConvexHull convexHull in convexHulls)
|
||||||
|
{
|
||||||
|
if (!convexHull.Enabled || !convexHull.Intersects(camView)) { continue; }
|
||||||
|
if (convexHull.ParentEntity is Structure { CastShadow: false }) { continue; }
|
||||||
|
convexHull.DebugDraw(spriteBatch);
|
||||||
|
}
|
||||||
|
spriteBatch.End();
|
||||||
|
}
|
||||||
|
|
||||||
public void ClearLights()
|
public void ClearLights()
|
||||||
{
|
{
|
||||||
lights.Clear();
|
lights.Clear();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
public bool Persistent;
|
public bool Persistent;
|
||||||
|
|
||||||
|
|
||||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; } = new Dictionary<Identifier, SerializableProperty>();
|
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; } = new Dictionary<Identifier, SerializableProperty>();
|
||||||
|
|
||||||
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes, alwaysUseInstanceValues: true), Editable]
|
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes, alwaysUseInstanceValues: true), Editable]
|
||||||
@@ -228,6 +229,7 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
//do we need to recalculate the vertices of the light volume
|
//do we need to recalculate the vertices of the light volume
|
||||||
private bool needsRecalculation;
|
private bool needsRecalculation;
|
||||||
|
private bool needsRecalculationWhenUpToDate;
|
||||||
public bool NeedsRecalculation
|
public bool NeedsRecalculation
|
||||||
{
|
{
|
||||||
get { return needsRecalculation; }
|
get { return needsRecalculation; }
|
||||||
@@ -241,12 +243,30 @@ namespace Barotrauma.Lights
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
needsRecalculation = value;
|
needsRecalculation = value;
|
||||||
|
if (needsRecalculation && state != LightVertexState.UpToDate)
|
||||||
|
{
|
||||||
|
//if we're currently recalculating light vertices, mark that we need to recalculate them again after it's done
|
||||||
|
needsRecalculationWhenUpToDate = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//when were the vertices of the light volume last calculated
|
//when were the vertices of the light volume last calculated
|
||||||
public float LastRecalculationTime { get; private set; }
|
public float LastRecalculationTime { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
private enum LightVertexState
|
||||||
|
{
|
||||||
|
UpToDate,
|
||||||
|
PendingRayCasts,
|
||||||
|
PendingVertexRecalculation,
|
||||||
|
}
|
||||||
|
|
||||||
|
private LightVertexState state;
|
||||||
|
|
||||||
|
private Vector2 calculatedDrawPos;
|
||||||
|
|
||||||
private readonly Dictionary<Submarine, Vector2> diffToSub;
|
private readonly Dictionary<Submarine, Vector2> diffToSub;
|
||||||
|
|
||||||
private DynamicVertexBuffer lightVolumeBuffer;
|
private DynamicVertexBuffer lightVolumeBuffer;
|
||||||
@@ -255,7 +275,6 @@ namespace Barotrauma.Lights
|
|||||||
private int indexCount;
|
private int indexCount;
|
||||||
|
|
||||||
private Vector2 translateVertices;
|
private Vector2 translateVertices;
|
||||||
private float rotateVertices;
|
|
||||||
|
|
||||||
private readonly LightSourceParams lightSourceParams;
|
private readonly LightSourceParams lightSourceParams;
|
||||||
|
|
||||||
@@ -295,7 +314,6 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
if (Math.Abs(rotation - prevCalculatedRotation) < RotationRecalculationThreshold && vertices != null)
|
if (Math.Abs(rotation - prevCalculatedRotation) < RotationRecalculationThreshold && vertices != null)
|
||||||
{
|
{
|
||||||
rotateVertices = rotation - prevCalculatedRotation;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,13 +665,19 @@ namespace Barotrauma.Lights
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly List<Segment> visibleSegments = new List<Segment>();
|
private static readonly object mutex = new object();
|
||||||
private static readonly List<SegmentPoint> points = new List<SegmentPoint>();
|
|
||||||
private static readonly List<Vector2> output = new List<Vector2>();
|
private readonly List<Segment> visibleSegments = new List<Segment>();
|
||||||
private static readonly SegmentPoint[] boundaryCorners = new SegmentPoint[4];
|
private readonly List<SegmentPoint> points = new List<SegmentPoint>();
|
||||||
private List<Vector2> FindRaycastHits()
|
private readonly List<Vector2> verts = new List<Vector2>();
|
||||||
|
private readonly SegmentPoint[] boundaryCorners = new SegmentPoint[4];
|
||||||
|
private void FindRaycastHits()
|
||||||
{
|
{
|
||||||
if (!CastShadows || Range < 1.0f || Color.A < 1) { return null; }
|
if (!CastShadows || Range < 1.0f || Color.A < 1)
|
||||||
|
{
|
||||||
|
state = LightVertexState.PendingVertexRecalculation;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Vector2 drawPos = position;
|
Vector2 drawPos = position;
|
||||||
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
|
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
|
||||||
@@ -666,8 +690,18 @@ namespace Barotrauma.Lights
|
|||||||
if (!chList.IsHidden.Contains(hull))
|
if (!chList.IsHidden.Contains(hull))
|
||||||
{
|
{
|
||||||
//find convexhull segments that are close enough and facing towards the light source
|
//find convexhull segments that are close enough and facing towards the light source
|
||||||
hull.RefreshWorldPositions();
|
lock (mutex)
|
||||||
hull.GetVisibleSegments(drawPos, visibleSegments, ignoreEdges: false);
|
{
|
||||||
|
hull.RefreshWorldPositions();
|
||||||
|
hull.GetVisibleSegments(drawPos, visibleSegments);
|
||||||
|
foreach (var visibleSegment in visibleSegments)
|
||||||
|
{
|
||||||
|
if (visibleSegment.ConvexHull?.ParentEntity?.Submarine != null)
|
||||||
|
{
|
||||||
|
visibleSegment.SubmarineDrawPos = visibleSegment.ConvexHull.ParentEntity.Submarine.DrawPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (ConvexHull hull in chList.List)
|
foreach (ConvexHull hull in chList.List)
|
||||||
@@ -676,17 +710,19 @@ namespace Barotrauma.Lights
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//add a square-shaped boundary to make sure we've got something to construct the triangles from
|
state = LightVertexState.PendingRayCasts;
|
||||||
//even if there aren't enough hull segments around the light source
|
GameMain.LightManager.AddRayCastTask(this, drawPos, rotation);
|
||||||
|
}
|
||||||
|
|
||||||
//(might be more effective to calculate if we actually need these extra points)
|
|
||||||
|
|
||||||
|
public void RayCastTask(Vector2 drawPos, float rotation)
|
||||||
|
{
|
||||||
Vector2 drawOffset = Vector2.Zero;
|
Vector2 drawOffset = Vector2.Zero;
|
||||||
float boundsExtended = TextureRange;
|
float boundsExtended = TextureRange;
|
||||||
if (OverrideLightTexture != null)
|
if (OverrideLightTexture != null)
|
||||||
{
|
{
|
||||||
float cosAngle = (float)Math.Cos(Rotation);
|
float cosAngle = (float)Math.Cos(rotation);
|
||||||
float sinAngle = -(float)Math.Sin(Rotation);
|
float sinAngle = -(float)Math.Sin(rotation);
|
||||||
|
|
||||||
var overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
|
var overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
|
||||||
|
|
||||||
@@ -706,6 +742,10 @@ namespace Barotrauma.Lights
|
|||||||
drawOffset.Y = origin.X * sinAngle + origin.Y * cosAngle;
|
drawOffset.Y = origin.X * sinAngle + origin.Y * cosAngle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//add a square-shaped boundary to make sure we've got something to construct the triangles from
|
||||||
|
//even if there aren't enough hull segments around the light source
|
||||||
|
|
||||||
|
//(might be more effective to calculate if we actually need these extra points)
|
||||||
Vector2 boundsMin = drawPos + drawOffset + new Vector2(-boundsExtended, -boundsExtended);
|
Vector2 boundsMin = drawPos + drawOffset + new Vector2(-boundsExtended, -boundsExtended);
|
||||||
Vector2 boundsMax = drawPos + drawOffset + new Vector2(boundsExtended, boundsExtended);
|
Vector2 boundsMax = drawPos + drawOffset + new Vector2(boundsExtended, boundsExtended);
|
||||||
boundaryCorners[0] = new SegmentPoint(boundsMax, null);
|
boundaryCorners[0] = new SegmentPoint(boundsMax, null);
|
||||||
@@ -719,197 +759,197 @@ namespace Barotrauma.Lights
|
|||||||
visibleSegments.Add(s);
|
visibleSegments.Add(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Generate new points at the intersections between segments
|
lock (mutex)
|
||||||
//This is necessary for the light volume to generate properly on some subs
|
|
||||||
for (int i = 0; i < visibleSegments.Count; i++)
|
|
||||||
{
|
{
|
||||||
Vector2 p1a = visibleSegments[i].Start.WorldPos;
|
//Generate new points at the intersections between segments
|
||||||
Vector2 p1b = visibleSegments[i].End.WorldPos;
|
//This is necessary for the light volume to generate properly on some subs
|
||||||
|
for (int i = 0; i < visibleSegments.Count; i++)
|
||||||
for (int j = i + 1; j < visibleSegments.Count; j++)
|
|
||||||
{
|
{
|
||||||
//ignore intersections between parallel axis-aligned segments
|
Vector2 p1a = visibleSegments[i].Start.WorldPos;
|
||||||
if (visibleSegments[i].IsAxisAligned && visibleSegments[j].IsAxisAligned &&
|
Vector2 p1b = visibleSegments[i].End.WorldPos;
|
||||||
visibleSegments[i].IsHorizontal == visibleSegments[j].IsHorizontal)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 p2a = visibleSegments[j].Start.WorldPos;
|
for (int j = i + 1; j < visibleSegments.Count; j++)
|
||||||
Vector2 p2b = visibleSegments[j].End.WorldPos;
|
|
||||||
|
|
||||||
if (Vector2.DistanceSquared(p1a, p2a) < 5.0f ||
|
|
||||||
Vector2.DistanceSquared(p1a, p2b) < 5.0f ||
|
|
||||||
Vector2.DistanceSquared(p1b, p2a) < 5.0f ||
|
|
||||||
Vector2.DistanceSquared(p1b, p2b) < 5.0f)
|
|
||||||
{
|
{
|
||||||
continue;
|
//ignore intersections between parallel axis-aligned segments
|
||||||
}
|
if (visibleSegments[i].IsAxisAligned && visibleSegments[j].IsAxisAligned &&
|
||||||
|
visibleSegments[i].IsHorizontal == visibleSegments[j].IsHorizontal)
|
||||||
bool intersects;
|
|
||||||
Vector2 intersection = Vector2.Zero;
|
|
||||||
if (visibleSegments[i].IsAxisAligned)
|
|
||||||
{
|
|
||||||
intersects = MathUtils.GetAxisAlignedLineIntersection(p2a, p2b, p1a, p1b, visibleSegments[i].IsHorizontal, out intersection);
|
|
||||||
}
|
|
||||||
else if (visibleSegments[j].IsAxisAligned)
|
|
||||||
{
|
|
||||||
intersects = MathUtils.GetAxisAlignedLineIntersection(p1a, p1b, p2a, p2b, visibleSegments[j].IsHorizontal, out intersection);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
intersects = MathUtils.GetLineIntersection(p1a, p1b, p2a, p2b, out intersection);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intersects)
|
|
||||||
{
|
|
||||||
SegmentPoint start = visibleSegments[i].Start;
|
|
||||||
SegmentPoint end = visibleSegments[i].End;
|
|
||||||
SegmentPoint mid = new SegmentPoint(intersection, null);
|
|
||||||
if (visibleSegments[i].ConvexHull?.ParentEntity?.Submarine != null)
|
|
||||||
{
|
|
||||||
mid.Pos -= visibleSegments[i].ConvexHull.ParentEntity.Submarine.DrawPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Vector2.DistanceSquared(start.WorldPos, mid.WorldPos) < 5.0f ||
|
|
||||||
Vector2.DistanceSquared(end.WorldPos, mid.WorldPos) < 5.0f)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Segment seg1 = new Segment(start, mid, visibleSegments[i].ConvexHull)
|
Vector2 p2a = visibleSegments[j].Start.WorldPos;
|
||||||
{
|
Vector2 p2b = visibleSegments[j].End.WorldPos;
|
||||||
IsHorizontal = visibleSegments[i].IsHorizontal,
|
|
||||||
};
|
|
||||||
|
|
||||||
Segment seg2 = new Segment(mid, end, visibleSegments[i].ConvexHull)
|
if (Vector2.DistanceSquared(p1a, p2a) < 5.0f ||
|
||||||
|
Vector2.DistanceSquared(p1a, p2b) < 5.0f ||
|
||||||
|
Vector2.DistanceSquared(p1b, p2a) < 5.0f ||
|
||||||
|
Vector2.DistanceSquared(p1b, p2b) < 5.0f)
|
||||||
{
|
{
|
||||||
IsHorizontal = visibleSegments[i].IsHorizontal
|
continue;
|
||||||
};
|
}
|
||||||
|
|
||||||
visibleSegments[i] = seg1;
|
bool intersects;
|
||||||
visibleSegments.Insert(i + 1, seg2);
|
Vector2 intersection = Vector2.Zero;
|
||||||
|
if (visibleSegments[i].IsAxisAligned)
|
||||||
|
{
|
||||||
|
intersects = MathUtils.GetAxisAlignedLineIntersection(p2a, p2b, p1a, p1b, visibleSegments[i].IsHorizontal, out intersection);
|
||||||
|
}
|
||||||
|
else if (visibleSegments[j].IsAxisAligned)
|
||||||
|
{
|
||||||
|
intersects = MathUtils.GetAxisAlignedLineIntersection(p1a, p1b, p2a, p2b, visibleSegments[j].IsHorizontal, out intersection);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
intersects = MathUtils.GetLineSegmentIntersection(p1a, p1b, p2a, p2b, out intersection);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intersects)
|
||||||
|
{
|
||||||
|
SegmentPoint start = visibleSegments[i].Start;
|
||||||
|
SegmentPoint end = visibleSegments[i].End;
|
||||||
|
SegmentPoint mid = new SegmentPoint(intersection, null);
|
||||||
|
mid.Pos -= visibleSegments[i].SubmarineDrawPos;
|
||||||
|
|
||||||
|
if (Vector2.DistanceSquared(start.WorldPos, mid.WorldPos) < 5.0f ||
|
||||||
|
Vector2.DistanceSquared(end.WorldPos, mid.WorldPos) < 5.0f)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Segment seg1 = new Segment(start, mid, visibleSegments[i].ConvexHull)
|
||||||
|
{
|
||||||
|
IsHorizontal = visibleSegments[i].IsHorizontal,
|
||||||
|
};
|
||||||
|
|
||||||
|
Segment seg2 = new Segment(mid, end, visibleSegments[i].ConvexHull)
|
||||||
|
{
|
||||||
|
IsHorizontal = visibleSegments[i].IsHorizontal
|
||||||
|
};
|
||||||
|
|
||||||
|
visibleSegments[i] = seg1;
|
||||||
|
visibleSegments.Insert(i + 1, seg2);
|
||||||
|
i--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
points.Clear();
|
||||||
|
//remove segments that fall out of bounds
|
||||||
|
for (int i = 0; i < visibleSegments.Count; i++)
|
||||||
|
{
|
||||||
|
Segment s = visibleSegments[i];
|
||||||
|
if (Math.Abs(s.Start.WorldPos.X - drawPos.X - drawOffset.X) > boundsExtended + 1.0f ||
|
||||||
|
Math.Abs(s.Start.WorldPos.Y - drawPos.Y - drawOffset.Y) > boundsExtended + 1.0f ||
|
||||||
|
Math.Abs(s.End.WorldPos.X - drawPos.X - drawOffset.X) > boundsExtended + 1.0f ||
|
||||||
|
Math.Abs(s.End.WorldPos.Y - drawPos.Y - drawOffset.Y) > boundsExtended + 1.0f)
|
||||||
|
{
|
||||||
|
visibleSegments.RemoveAt(i);
|
||||||
i--;
|
i--;
|
||||||
break;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
points.Add(s.Start);
|
||||||
|
points.Add(s.End);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
points.Clear();
|
//remove points that are very close to each other
|
||||||
//remove segments that fall out of bounds
|
for (int i = 0; i < points.Count; i++)
|
||||||
for (int i = 0; i < visibleSegments.Count; i++)
|
|
||||||
{
|
|
||||||
Segment s = visibleSegments[i];
|
|
||||||
if (Math.Abs(s.Start.WorldPos.X - drawPos.X - drawOffset.X) > boundsExtended + 1.0f ||
|
|
||||||
Math.Abs(s.Start.WorldPos.Y - drawPos.Y - drawOffset.Y) > boundsExtended + 1.0f ||
|
|
||||||
Math.Abs(s.End.WorldPos.X - drawPos.X - drawOffset.X) > boundsExtended + 1.0f ||
|
|
||||||
Math.Abs(s.End.WorldPos.Y - drawPos.Y - drawOffset.Y) > boundsExtended + 1.0f)
|
|
||||||
{
|
{
|
||||||
visibleSegments.RemoveAt(i);
|
for (int j = Math.Min(i + 4, points.Count - 1); j > i; j--)
|
||||||
i--;
|
{
|
||||||
|
if (Math.Abs(points[i].WorldPos.X - points[j].WorldPos.X) < 6 &&
|
||||||
|
Math.Abs(points[i].WorldPos.Y - points[j].WorldPos.Y) < 6)
|
||||||
|
{
|
||||||
|
points.RemoveAt(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
var compareCCW = new CompareSegmentPointCW(drawPos);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
points.Add(s.Start);
|
points.Sort(compareCCW);
|
||||||
points.Add(s.End);
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder("Constructing light volumes failed! Light pos: " + drawPos + ", Hull verts:\n");
|
||||||
|
foreach (SegmentPoint sp in points)
|
||||||
|
{
|
||||||
|
sb.AppendLine(sp.Pos.ToString());
|
||||||
|
}
|
||||||
|
DebugConsole.ThrowError(sb.ToString(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
visibleSegments.Sort((s1, s2) =>
|
||||||
|
MathUtils.LineToPointDistanceSquared(s1.Start.WorldPos, s1.End.WorldPos, drawPos)
|
||||||
|
.CompareTo(MathUtils.LineToPointDistanceSquared(s2.Start.WorldPos, s2.End.WorldPos, drawPos)));
|
||||||
|
|
||||||
|
verts.Clear();
|
||||||
|
foreach (SegmentPoint p in points)
|
||||||
|
{
|
||||||
|
Vector2 dir = Vector2.Normalize(p.WorldPos - drawPos);
|
||||||
|
Vector2 dirNormal = new Vector2(-dir.Y, dir.X) * 3;
|
||||||
|
|
||||||
|
//do two slightly offset raycasts to hit the segment itself and whatever's behind it
|
||||||
|
var intersection1 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 - dirNormal, visibleSegments);
|
||||||
|
if (intersection1.index < 0) { return; }
|
||||||
|
var intersection2 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 + dirNormal, visibleSegments);
|
||||||
|
if (intersection2.index < 0) { return; }
|
||||||
|
|
||||||
|
Segment seg1 = visibleSegments[intersection1.index];
|
||||||
|
Segment seg2 = visibleSegments[intersection2.index];
|
||||||
|
|
||||||
|
bool isPoint1 = MathUtils.LineToPointDistanceSquared(seg1.Start.WorldPos, seg1.End.WorldPos, p.WorldPos) < 25.0f;
|
||||||
|
bool isPoint2 = MathUtils.LineToPointDistanceSquared(seg2.Start.WorldPos, seg2.End.WorldPos, p.WorldPos) < 25.0f;
|
||||||
|
|
||||||
|
if (isPoint1 && isPoint2)
|
||||||
|
{
|
||||||
|
//hit at the current segmentpoint -> place the segmentpoint into the list
|
||||||
|
verts.Add(p.WorldPos);
|
||||||
|
|
||||||
|
foreach (ConvexHullList hullList in convexHullsInRange)
|
||||||
|
{
|
||||||
|
hullList.IsHidden.Remove(p.ConvexHull);
|
||||||
|
hullList.IsHidden.Remove(seg1.ConvexHull);
|
||||||
|
hullList.IsHidden.Remove(seg2.ConvexHull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (intersection1.index != intersection2.index)
|
||||||
|
{
|
||||||
|
//the raycasts landed on different segments
|
||||||
|
//we definitely want to generate new geometry here
|
||||||
|
verts.Add(isPoint1 ? p.WorldPos : intersection1.pos);
|
||||||
|
verts.Add(isPoint2 ? p.WorldPos : intersection2.pos);
|
||||||
|
|
||||||
|
foreach (ConvexHullList hullList in convexHullsInRange)
|
||||||
|
{
|
||||||
|
hullList.IsHidden.Remove(p.ConvexHull);
|
||||||
|
hullList.IsHidden.Remove(seg1.ConvexHull);
|
||||||
|
hullList.IsHidden.Remove(seg2.ConvexHull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if neither of the conditions above are met, we just assume
|
||||||
|
//that the raycasts both resulted on the same segment
|
||||||
|
//and creating geometry here would be wasteful
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//remove points that are very close to each other
|
//remove points that are very close to each other
|
||||||
for (int i = 0; i < points.Count; i++)
|
for (int i = 0; i < verts.Count - 1; i++)
|
||||||
{
|
{
|
||||||
for (int j = Math.Min(i + 4, points.Count-1); j > i; j--)
|
for (int j = Math.Min(i + 4, verts.Count - 1); j > i; j--)
|
||||||
{
|
{
|
||||||
if (Math.Abs(points[i].WorldPos.X - points[j].WorldPos.X) < 6 &&
|
if (Math.Abs(verts[i].X - verts[j].X) < 6 &&
|
||||||
Math.Abs(points[i].WorldPos.Y - points[j].WorldPos.Y) < 6)
|
Math.Abs(verts[i].Y - verts[j].Y) < 6)
|
||||||
{
|
{
|
||||||
points.RemoveAt(j);
|
verts.RemoveAt(j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
calculatedDrawPos = drawPos;
|
||||||
var compareCCW = new CompareSegmentPointCW(drawPos);
|
state = LightVertexState.PendingVertexRecalculation;
|
||||||
try
|
|
||||||
{
|
|
||||||
points.Sort(compareCCW);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder("Constructing light volumes failed! Light pos: " + drawPos + ", Hull verts:\n");
|
|
||||||
foreach (SegmentPoint sp in points)
|
|
||||||
{
|
|
||||||
sb.AppendLine(sp.Pos.ToString());
|
|
||||||
}
|
|
||||||
DebugConsole.ThrowError(sb.ToString(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
visibleSegments.Sort((s1, s2) =>
|
|
||||||
MathUtils.LineToPointDistanceSquared(s1.Start.WorldPos, s1.End.WorldPos, drawPos)
|
|
||||||
.CompareTo(MathUtils.LineToPointDistanceSquared(s2.Start.WorldPos, s2.End.WorldPos, drawPos)));
|
|
||||||
|
|
||||||
output.Clear();
|
|
||||||
foreach (SegmentPoint p in points)
|
|
||||||
{
|
|
||||||
Vector2 dir = Vector2.Normalize(p.WorldPos - drawPos);
|
|
||||||
Vector2 dirNormal = new Vector2(-dir.Y, dir.X) * 3;
|
|
||||||
|
|
||||||
//do two slightly offset raycasts to hit the segment itself and whatever's behind it
|
|
||||||
var intersection1 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 - dirNormal, visibleSegments);
|
|
||||||
if (intersection1.index < 0) { return null; }
|
|
||||||
var intersection2 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 + dirNormal, visibleSegments);
|
|
||||||
if (intersection2.index < 0) { return null; }
|
|
||||||
|
|
||||||
Segment seg1 = visibleSegments[intersection1.index];
|
|
||||||
Segment seg2 = visibleSegments[intersection2.index];
|
|
||||||
|
|
||||||
bool isPoint1 = MathUtils.LineToPointDistanceSquared(seg1.Start.WorldPos, seg1.End.WorldPos, p.WorldPos) < 25.0f;
|
|
||||||
bool isPoint2 = MathUtils.LineToPointDistanceSquared(seg2.Start.WorldPos, seg2.End.WorldPos, p.WorldPos) < 25.0f;
|
|
||||||
|
|
||||||
if (isPoint1 && isPoint2)
|
|
||||||
{
|
|
||||||
//hit at the current segmentpoint -> place the segmentpoint into the list
|
|
||||||
output.Add(p.WorldPos);
|
|
||||||
|
|
||||||
foreach (ConvexHullList hullList in convexHullsInRange)
|
|
||||||
{
|
|
||||||
hullList.IsHidden.Remove(p.ConvexHull);
|
|
||||||
hullList.IsHidden.Remove(seg1.ConvexHull);
|
|
||||||
hullList.IsHidden.Remove(seg2.ConvexHull);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (intersection1.index != intersection2.index)
|
|
||||||
{
|
|
||||||
//the raycasts landed on different segments
|
|
||||||
//we definitely want to generate new geometry here
|
|
||||||
output.Add(isPoint1 ? p.WorldPos : intersection1.pos);
|
|
||||||
output.Add(isPoint2 ? p.WorldPos : intersection2.pos);
|
|
||||||
|
|
||||||
foreach (ConvexHullList hullList in convexHullsInRange)
|
|
||||||
{
|
|
||||||
hullList.IsHidden.Remove(p.ConvexHull);
|
|
||||||
hullList.IsHidden.Remove(seg1.ConvexHull);
|
|
||||||
hullList.IsHidden.Remove(seg2.ConvexHull);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//if neither of the conditions above are met, we just assume
|
|
||||||
//that the raycasts both resulted on the same segment
|
|
||||||
//and creating geometry here would be wasteful
|
|
||||||
}
|
|
||||||
|
|
||||||
//remove points that are very close to each other
|
|
||||||
for (int i = 0; i < output.Count - 1; i++)
|
|
||||||
{
|
|
||||||
for (int j = Math.Min(i + 4, output.Count - 1); j > i; j--)
|
|
||||||
{
|
|
||||||
if (Math.Abs(output[i].X - output[j].X) < 6 &&
|
|
||||||
Math.Abs(output[i].Y - output[j].Y) < 6)
|
|
||||||
{
|
|
||||||
output.RemoveAt(j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (int index, Vector2 pos) RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments)
|
private static (int index, Vector2 pos) RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments)
|
||||||
@@ -954,7 +994,7 @@ namespace Barotrauma.Lights
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
intersects = MathUtils.GetLineIntersection(rayStart, rayEnd, s.Start.WorldPos, s.End.WorldPos, out intersection);
|
intersects = MathUtils.GetLineSegmentIntersection(rayStart, rayEnd, s.Start.WorldPos, s.End.WorldPos, out intersection);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intersects)
|
if (intersects)
|
||||||
@@ -987,8 +1027,7 @@ namespace Barotrauma.Lights
|
|||||||
indices = new short[indexCount];
|
indices = new short[indexCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 drawPos = position;
|
Vector2 drawPos = calculatedDrawPos;
|
||||||
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
|
|
||||||
|
|
||||||
float cosAngle = (float)Math.Cos(Rotation);
|
float cosAngle = (float)Math.Cos(Rotation);
|
||||||
float sinAngle = -(float)Math.Sin(Rotation);
|
float sinAngle = -(float)Math.Sin(Rotation);
|
||||||
@@ -1042,7 +1081,7 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
//calculate normal of first segment
|
//calculate normal of first segment
|
||||||
Vector2 nDiff1 = vertex - nextVertex;
|
Vector2 nDiff1 = vertex - nextVertex;
|
||||||
float tx = nDiff1.X; nDiff1.X = -nDiff1.Y; nDiff1.Y = tx;
|
nDiff1 = new Vector2(-nDiff1.Y, nDiff1.X);
|
||||||
nDiff1 /= Math.Max(Math.Abs(nDiff1.X), Math.Abs(nDiff1.Y));
|
nDiff1 /= Math.Max(Math.Abs(nDiff1.X), Math.Abs(nDiff1.Y));
|
||||||
//if the normal is pointing towards the light origin
|
//if the normal is pointing towards the light origin
|
||||||
//rather than away from it, invert it
|
//rather than away from it, invert it
|
||||||
@@ -1050,21 +1089,23 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
//calculate normal of second segment
|
//calculate normal of second segment
|
||||||
Vector2 nDiff2 = prevVertex - vertex;
|
Vector2 nDiff2 = prevVertex - vertex;
|
||||||
tx = nDiff2.X; nDiff2.X = -nDiff2.Y; nDiff2.Y = tx;
|
nDiff2 = new Vector2(-nDiff2.Y, nDiff2.X);
|
||||||
nDiff2 /= Math.Max(Math.Abs(nDiff2.X),Math.Abs(nDiff2.Y));
|
nDiff2 /= Math.Max(Math.Abs(nDiff2.X), Math.Abs(nDiff2.Y));
|
||||||
//if the normal is pointing towards the light origin
|
//if the normal is pointing towards the light origin
|
||||||
//rather than away from it, invert it
|
//rather than away from it, invert it
|
||||||
if (Vector2.DistanceSquared(nDiff2, rawDiff) > Vector2.DistanceSquared(-nDiff2, rawDiff)) nDiff2 = -nDiff2;
|
if (Vector2.DistanceSquared(nDiff2, rawDiff) > Vector2.DistanceSquared(-nDiff2, rawDiff)) nDiff2 = -nDiff2;
|
||||||
|
|
||||||
//add the normals together and use some magic numbers to create
|
//add the normals together and use some magic numbers to create
|
||||||
//a somewhat useful/good-looking blur
|
//a somewhat useful/good-looking blur
|
||||||
Vector2 nDiff = nDiff1 * 40.0f;
|
float blurDistance = 40.0f;
|
||||||
if (MathUtils.GetLineIntersection(vertex + (nDiff1 * 40.0f), nextVertex + (nDiff1 * 40.0f), vertex + (nDiff2 * 40.0f), prevVertex + (nDiff2 * 40.0f), true, out Vector2 intersection))
|
Vector2 nDiff = nDiff1 * blurDistance;
|
||||||
|
if (MathUtils.GetLineIntersection(vertex + (nDiff1 * blurDistance), nextVertex + (nDiff1 * blurDistance), vertex + (nDiff2 * blurDistance), prevVertex + (nDiff2 * blurDistance), true, out Vector2 intersection))
|
||||||
{
|
{
|
||||||
nDiff = intersection - vertex;
|
nDiff = intersection - vertex;
|
||||||
if (nDiff.LengthSquared() > 10000.0f)
|
if (nDiff.LengthSquared() > 100.0f * 100.0f)
|
||||||
{
|
{
|
||||||
nDiff /= Math.Max(Math.Abs(nDiff.X), Math.Abs(nDiff.Y)); nDiff *= 100.0f;
|
nDiff /= Math.Max(Math.Abs(nDiff.X), Math.Abs(nDiff.Y));
|
||||||
|
nDiff *= 100.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1162,7 +1203,6 @@ namespace Barotrauma.Lights
|
|||||||
}
|
}
|
||||||
|
|
||||||
translateVertices = Vector2.Zero;
|
translateVertices = Vector2.Zero;
|
||||||
rotateVertices = 0.0f;
|
|
||||||
prevCalculatedPosition = position;
|
prevCalculatedPosition = position;
|
||||||
prevCalculatedRotation = rotation;
|
prevCalculatedRotation = rotation;
|
||||||
}
|
}
|
||||||
@@ -1340,31 +1380,41 @@ namespace Barotrauma.Lights
|
|||||||
|
|
||||||
if (NeedsRecalculation && allowRecalculation)
|
if (NeedsRecalculation && allowRecalculation)
|
||||||
{
|
{
|
||||||
recalculationCount++;
|
if (state == LightVertexState.UpToDate)
|
||||||
var verts = FindRaycastHits();
|
|
||||||
if (verts == null)
|
|
||||||
{
|
{
|
||||||
#if DEBUG
|
recalculationCount++;
|
||||||
DebugConsole.ThrowError($"Failed to generate vertices for a light source. Range: {Range}, color: {Color}, brightness: {CurrentBrightness}, parent: {ParentBody?.UserData ?? "Unknown"}");
|
FindRaycastHits();
|
||||||
#endif
|
|
||||||
Enabled = false;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (state == LightVertexState.PendingVertexRecalculation)
|
||||||
|
{
|
||||||
|
if (verts == null)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
DebugConsole.ThrowError($"Failed to generate vertices for a light source. Range: {Range}, color: {Color}, brightness: {CurrentBrightness}, parent: {ParentBody?.UserData ?? "Unknown"}");
|
||||||
|
#endif
|
||||||
|
Enabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CalculateLightVertices(verts);
|
CalculateLightVertices(verts);
|
||||||
|
|
||||||
LastRecalculationTime = (float)Timing.TotalTime;
|
LastRecalculationTime = (float)Timing.TotalTime;
|
||||||
NeedsRecalculation = false;
|
NeedsRecalculation = needsRecalculationWhenUpToDate;
|
||||||
|
needsRecalculationWhenUpToDate = false;
|
||||||
|
|
||||||
|
state = LightVertexState.UpToDate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vertexCount == 0) { return; }
|
||||||
|
|
||||||
Vector2 offset = ParentSub == null ? Vector2.Zero : ParentSub.DrawPosition;
|
Vector2 offset = ParentSub == null ? Vector2.Zero : ParentSub.DrawPosition;
|
||||||
lightEffect.World =
|
lightEffect.World =
|
||||||
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
|
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
|
||||||
Matrix.CreateRotationZ(rotateVertices - MathHelper.ToRadians(LightSourceParams.Rotation)) *
|
Matrix.CreateRotationZ(MathHelper.ToRadians(LightSourceParams.Rotation)) *
|
||||||
Matrix.CreateTranslation(new Vector3(position + offset + translateVertices, 0.0f)) *
|
Matrix.CreateTranslation(new Vector3(position + offset + translateVertices, 0.0f)) *
|
||||||
transform;
|
transform;
|
||||||
|
|
||||||
if (vertexCount == 0) { return; }
|
|
||||||
|
|
||||||
lightEffect.DiffuseColor = (new Vector3(Color.R, Color.G, Color.B) * (Color.A / 255.0f * CurrentBrightness)) / 255.0f;
|
lightEffect.DiffuseColor = (new Vector3(Color.R, Color.G, Color.B) * (Color.A / 255.0f * CurrentBrightness)) / 255.0f;
|
||||||
if (OverrideLightTexture != null)
|
if (OverrideLightTexture != null)
|
||||||
|
|||||||
@@ -738,8 +738,8 @@ namespace Barotrauma
|
|||||||
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, rect);
|
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, rect);
|
||||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||||
|
|
||||||
Vector2 topLeft = rectCenter + viewOffset;
|
Vector2 topLeft = rectCenter + viewOffset - rect.Location.ToVector2();
|
||||||
Vector2 bottomRight = rectCenter + (viewOffset + new Vector2(Width, Height));
|
Vector2 bottomRight = topLeft + new Vector2(Width, Height);
|
||||||
Vector2 mapTileSize = mapTiles[0, 0].size * generationParams.MapTileScale;
|
Vector2 mapTileSize = mapTiles[0, 0].size * generationParams.MapTileScale;
|
||||||
|
|
||||||
int startX = (int)Math.Floor(-topLeft.X / mapTileSize.X) - 1;
|
int startX = (int)Math.Floor(-topLeft.X / mapTileSize.X) - 1;
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static readonly List<RoundSound> roundSounds = new List<RoundSound>();
|
private static readonly List<RoundSound> roundSounds = new List<RoundSound>();
|
||||||
|
private static readonly Dictionary<string, RoundSound> roundSoundByPath = new Dictionary<string, RoundSound>();
|
||||||
public static RoundSound? Load(ContentXElement element, bool stream = false)
|
public static RoundSound? Load(ContentXElement element, bool stream = false)
|
||||||
{
|
{
|
||||||
if (GameMain.SoundManager?.Disabled ?? true) { return null; }
|
if (GameMain.SoundManager?.Disabled ?? true) { return null; }
|
||||||
|
|
||||||
var filename = element.GetAttributeContentPath("file") ?? element.GetAttributeContentPath("sound");
|
var filename = element.GetAttributeContentPath("file") ?? element.GetAttributeContentPath("sound");
|
||||||
|
|
||||||
if (filename is null)
|
if (filename is null)
|
||||||
{
|
{
|
||||||
string errorMsg = "Error when loading round sound (" + element + ") - file path not set";
|
string errorMsg = "Error when loading round sound (" + element + ") - file path not set";
|
||||||
@@ -70,7 +70,11 @@ namespace Barotrauma
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound? existingSound = roundSounds.Find(s => s.Filename == filename?.FullPath && s.Stream == stream && s.Sound is { Disposed: false })?.Sound;
|
Sound? existingSound = null;
|
||||||
|
if (roundSoundByPath.TryGetValue(filename.FullPath, out RoundSound? rs) && rs.Sound is { Disposed: false })
|
||||||
|
{
|
||||||
|
existingSound = rs.Sound;
|
||||||
|
}
|
||||||
|
|
||||||
if (existingSound is null)
|
if (existingSound is null)
|
||||||
{
|
{
|
||||||
@@ -99,7 +103,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
RoundSound newSound = new RoundSound(element, existingSound);
|
RoundSound newSound = new RoundSound(element, existingSound);
|
||||||
|
if (filename is not null && !newSound.Stream)
|
||||||
|
{
|
||||||
|
roundSoundByPath.TryAdd(filename.FullPath, newSound);
|
||||||
|
}
|
||||||
roundSounds.Add(newSound);
|
roundSounds.Add(newSound);
|
||||||
return newSound;
|
return newSound;
|
||||||
}
|
}
|
||||||
@@ -124,24 +131,14 @@ namespace Barotrauma
|
|||||||
roundSound.Sound = existingSound;
|
roundSound.Sound = existingSound;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Remove(RoundSound roundSound)
|
|
||||||
{
|
|
||||||
#warning TODO: what is going on here????
|
|
||||||
roundSound.Sound?.Dispose();
|
|
||||||
|
|
||||||
if (roundSounds.Contains(roundSound)) { roundSounds.Remove(roundSound); }
|
|
||||||
foreach (RoundSound otherSound in roundSounds)
|
|
||||||
{
|
|
||||||
if (otherSound.Sound == roundSound.Sound) { otherSound.Sound = null; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveAllRoundSounds()
|
public static void RemoveAllRoundSounds()
|
||||||
{
|
{
|
||||||
for (int i = roundSounds.Count - 1; i >= 0; i--)
|
foreach (var roundSound in roundSounds)
|
||||||
{
|
{
|
||||||
Remove(roundSounds[i]);
|
roundSound.Sound?.Dispose();
|
||||||
}
|
}
|
||||||
|
roundSounds.Clear();
|
||||||
|
roundSoundByPath.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,21 +55,11 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (!CastShadow) { return; }
|
if (!CastShadow) { return; }
|
||||||
|
|
||||||
if (convexHulls == null)
|
convexHulls ??= new List<ConvexHull>();
|
||||||
{
|
var h = new ConvexHull(
|
||||||
convexHulls = new List<ConvexHull>();
|
new Rectangle((position - size / 2).ToPoint(), size.ToPoint()),
|
||||||
}
|
IsHorizontal,
|
||||||
|
this);
|
||||||
Vector2 halfSize = size / 2;
|
|
||||||
Vector2[] verts = new Vector2[]
|
|
||||||
{
|
|
||||||
position + new Vector2(-halfSize.X, halfSize.Y),
|
|
||||||
position + new Vector2(halfSize.X, halfSize.Y),
|
|
||||||
position + new Vector2(halfSize.X, -halfSize.Y),
|
|
||||||
position + new Vector2(-halfSize.X, -halfSize.Y),
|
|
||||||
};
|
|
||||||
|
|
||||||
var h = new ConvexHull(verts, Color.Black, this);
|
|
||||||
if (Math.Abs(rotation) > 0.001f)
|
if (Math.Abs(rotation) > 0.001f)
|
||||||
{
|
{
|
||||||
h.Rotate(position, rotation);
|
h.Rotate(position, rotation);
|
||||||
|
|||||||
@@ -116,11 +116,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
foreach (MapEntity e in entitiesToRender)
|
foreach (MapEntity e in entitiesToRender)
|
||||||
{
|
{
|
||||||
if (!e.DrawOverWater) continue;
|
if (!e.DrawOverWater) { continue; }
|
||||||
|
|
||||||
if (predicate != null)
|
if (predicate != null)
|
||||||
{
|
{
|
||||||
if (!predicate(e)) continue;
|
if (!predicate(e)) { continue; }
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Draw(spriteBatch, editing, false);
|
e.Draw(spriteBatch, editing, false);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using FarseerPhysics;
|
using FarseerPhysics;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -18,7 +19,7 @@ namespace Barotrauma
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Submarine> subsToMove = submarine.GetConnectedSubs();
|
var subsToMove = submarine.GetConnectedSubs();
|
||||||
foreach (Submarine dockedSub in subsToMove)
|
foreach (Submarine dockedSub in subsToMove)
|
||||||
{
|
{
|
||||||
if (dockedSub == submarine) { continue; }
|
if (dockedSub == submarine) { continue; }
|
||||||
@@ -51,7 +52,6 @@ namespace Barotrauma
|
|||||||
sub.PhysicsBody.SetTransformIgnoreContacts(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f);
|
sub.PhysicsBody.SetTransformIgnoreContacts(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closestSub != null && subsToMove.Contains(closestSub))
|
if (closestSub != null && subsToMove.Contains(closestSub))
|
||||||
{
|
{
|
||||||
GameMain.GameScreen.Cam.Position += moveAmount;
|
GameMain.GameScreen.Cam.Position += moveAmount;
|
||||||
|
|||||||
@@ -67,6 +67,10 @@ namespace Barotrauma
|
|||||||
else if (ConnectedDoor != null)
|
else if (ConnectedDoor != null)
|
||||||
{
|
{
|
||||||
sprite = iconSprites["Door"];
|
sprite = iconSprites["Door"];
|
||||||
|
if (ConnectedDoor.IsHorizontal && Ladders == null)
|
||||||
|
{
|
||||||
|
clr = Color.Yellow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (Ladders != null)
|
else if (Ladders != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3077,8 +3077,12 @@ namespace Barotrauma.Networking
|
|||||||
if (votingInterface != null)
|
if (votingInterface != null)
|
||||||
{
|
{
|
||||||
votingInterface.Update(deltaTime);
|
votingInterface.Update(deltaTime);
|
||||||
if (!votingInterface.VoteRunning)
|
if (!votingInterface.VoteRunning || votingInterface.TimedOut)
|
||||||
{
|
{
|
||||||
|
if (votingInterface.TimedOut)
|
||||||
|
{
|
||||||
|
DebugConsole.AddWarning($"Voting interface timed out.");
|
||||||
|
}
|
||||||
votingInterface.Remove();
|
votingInterface.Remove();
|
||||||
votingInterface = null;
|
votingInterface = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ namespace Barotrauma.Networking
|
|||||||
int botSpawnMode = 0,
|
int botSpawnMode = 0,
|
||||||
bool? useRespawnShuttle = null)
|
bool? useRespawnShuttle = null)
|
||||||
{
|
{
|
||||||
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
|
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) { return; }
|
||||||
|
|
||||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||||
|
|
||||||
|
|||||||
@@ -253,12 +253,9 @@ namespace Barotrauma.Networking
|
|||||||
//in push-to-talk mode, InputType.Voice uses the active chat mode
|
//in push-to-talk mode, InputType.Voice uses the active chat mode
|
||||||
bool usingActiveMode = PlayerInput.KeyDown(InputType.Voice);
|
bool usingActiveMode = PlayerInput.KeyDown(InputType.Voice);
|
||||||
bool pttDown = (usingActiveMode || usingLocalMode || usingRadioMode) && GUI.KeyboardDispatcher.Subscriber == null;
|
bool pttDown = (usingActiveMode || usingLocalMode || usingRadioMode) && GUI.KeyboardDispatcher.Subscriber == null;
|
||||||
if (pttDown || captureTimer <= 0)
|
|
||||||
{
|
|
||||||
ForceLocal = (usingActiveMode && GameMain.ActiveChatMode == ChatMode.Local) || usingLocalMode;
|
|
||||||
}
|
|
||||||
if (pttDown)
|
if (pttDown)
|
||||||
{
|
{
|
||||||
|
ForceLocal = (usingActiveMode && GameMain.ActiveChatMode == ChatMode.Local) || usingLocalMode;
|
||||||
allowEnqueue = true;
|
allowEnqueue = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,17 +115,20 @@ namespace Barotrauma.Networking
|
|||||||
float speechImpedimentMultiplier = 1.0f - client.Character.SpeechImpediment / 100.0f;
|
float speechImpedimentMultiplier = 1.0f - client.Character.SpeechImpediment / 100.0f;
|
||||||
bool spectating = Character.Controlled == null;
|
bool spectating = Character.Controlled == null;
|
||||||
float rangeMultiplier = spectating ? 2.0f : 1.0f;
|
float rangeMultiplier = spectating ? 2.0f : 1.0f;
|
||||||
WifiComponent radio = null;
|
WifiComponent senderRadio = null;
|
||||||
var messageType =
|
var messageType =
|
||||||
!client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) && ChatMessage.CanUseRadio(Character.Controlled) ?
|
!client.VoipQueue.ForceLocal &&
|
||||||
ChatMessageType.Radio : ChatMessageType.Default;
|
ChatMessage.CanUseRadio(client.Character, out senderRadio) &&
|
||||||
|
ChatMessage.CanUseRadio(Character.Controlled, out var recipientRadio) &&
|
||||||
|
senderRadio.CanReceive(recipientRadio) ?
|
||||||
|
ChatMessageType.Radio : ChatMessageType.Default;
|
||||||
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||||
|
|
||||||
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters;
|
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters;
|
||||||
client.RadioNoise = 0.0f;
|
client.RadioNoise = 0.0f;
|
||||||
if (messageType == ChatMessageType.Radio)
|
if (messageType == ChatMessageType.Radio)
|
||||||
{
|
{
|
||||||
client.VoipSound.SetRange(radio.Range * RangeNear * speechImpedimentMultiplier * rangeMultiplier, radio.Range * speechImpedimentMultiplier * rangeMultiplier);
|
client.VoipSound.SetRange(senderRadio.Range * RangeNear * speechImpedimentMultiplier * rangeMultiplier, senderRadio.Range * speechImpedimentMultiplier * rangeMultiplier);
|
||||||
if (distanceFactor > RangeNear && !spectating)
|
if (distanceFactor > RangeNear && !spectating)
|
||||||
{
|
{
|
||||||
//noise starts increasing exponentially after 40% range
|
//noise starts increasing exponentially after 40% range
|
||||||
|
|||||||
@@ -348,11 +348,13 @@ namespace Barotrauma
|
|||||||
switch (voteType)
|
switch (voteType)
|
||||||
{
|
{
|
||||||
case VoteType.PurchaseAndSwitchSub:
|
case VoteType.PurchaseAndSwitchSub:
|
||||||
GameMain.GameSession.PurchaseSubmarine(subInfo);
|
if (GameMain.GameSession.TryPurchaseSubmarine(subInfo))
|
||||||
GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems);
|
{
|
||||||
|
GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case VoteType.PurchaseSub:
|
case VoteType.PurchaseSub:
|
||||||
GameMain.GameSession.PurchaseSubmarine(subInfo);
|
GameMain.GameSession.TryPurchaseSubmarine(subInfo);
|
||||||
break;
|
break;
|
||||||
case VoteType.SwitchSub:
|
case VoteType.SwitchSub:
|
||||||
GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems);
|
GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using FarseerPhysics;
|
using Barotrauma.Extensions;
|
||||||
|
using FarseerPhysics;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using System;
|
using System;
|
||||||
@@ -49,18 +50,22 @@ namespace Barotrauma.Particles
|
|||||||
private Vector2 drawPosition;
|
private Vector2 drawPosition;
|
||||||
private float drawRotation;
|
private float drawRotation;
|
||||||
|
|
||||||
|
private Vector2 colliderRadius;
|
||||||
|
|
||||||
private Hull currentHull;
|
private Hull currentHull;
|
||||||
|
|
||||||
private List<Gap> hullGaps;
|
private List<Gap> hullGaps;
|
||||||
|
|
||||||
private bool hasSubEmitters;
|
private bool hasSubEmitters;
|
||||||
private List<ParticleEmitter> subEmitters = new List<ParticleEmitter>();
|
private readonly List<ParticleEmitter> subEmitters = new List<ParticleEmitter>();
|
||||||
|
|
||||||
private float animState;
|
private float animState;
|
||||||
private int animFrame;
|
private int animFrame;
|
||||||
|
|
||||||
private float collisionUpdateTimer;
|
private float collisionUpdateTimer;
|
||||||
|
|
||||||
|
private bool changesSize;
|
||||||
|
|
||||||
public bool HighQualityCollisionDetection;
|
public bool HighQualityCollisionDetection;
|
||||||
|
|
||||||
public Vector4 ColorMultiplier;
|
public Vector4 ColorMultiplier;
|
||||||
@@ -127,7 +132,10 @@ namespace Barotrauma.Particles
|
|||||||
position = (tracerPoints.Item1 + tracerPoints.Item2) / 2;
|
position = (tracerPoints.Item1 + tracerPoints.Item2) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefreshColliderSize();
|
||||||
|
|
||||||
sizeChange = prefab.SizeChangeMin + (prefab.SizeChangeMax - prefab.SizeChangeMin) * Rand.Range(0.0f, 1.0f);
|
sizeChange = prefab.SizeChangeMin + (prefab.SizeChangeMax - prefab.SizeChangeMin) * Rand.Range(0.0f, 1.0f);
|
||||||
|
changesSize = !sizeChange.NearlyEquals(Vector2.Zero);
|
||||||
|
|
||||||
this.position = position;
|
this.position = position;
|
||||||
prevPosition = position;
|
prevPosition = position;
|
||||||
@@ -256,8 +264,12 @@ namespace Barotrauma.Particles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size.X += sizeChange.X * deltaTime;
|
if (changesSize)
|
||||||
size.Y += sizeChange.Y * deltaTime;
|
{
|
||||||
|
size.X += sizeChange.X * deltaTime;
|
||||||
|
size.Y += sizeChange.Y * deltaTime;
|
||||||
|
RefreshColliderSize();
|
||||||
|
}
|
||||||
|
|
||||||
if (UseMiddleColor)
|
if (UseMiddleColor)
|
||||||
{
|
{
|
||||||
@@ -344,11 +356,11 @@ namespace Barotrauma.Particles
|
|||||||
{
|
{
|
||||||
Rectangle hullRect = currentHull.WorldRect;
|
Rectangle hullRect = currentHull.WorldRect;
|
||||||
Vector2 collisionNormal = Vector2.Zero;
|
Vector2 collisionNormal = Vector2.Zero;
|
||||||
if (velocity.Y < 0.0f && position.Y - prefab.CollisionRadius * size.Y < hullRect.Y - hullRect.Height)
|
if (velocity.Y < 0.0f && position.Y - colliderRadius.Y < hullRect.Y - hullRect.Height)
|
||||||
{
|
{
|
||||||
collisionNormal = new Vector2(0.0f, 1.0f);
|
collisionNormal = new Vector2(0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
else if (velocity.Y > 0.0f && position.Y + prefab.CollisionRadius * size.Y > hullRect.Y)
|
else if (velocity.Y > 0.0f && position.Y + colliderRadius.Y > hullRect.Y)
|
||||||
{
|
{
|
||||||
collisionNormal = new Vector2(0.0f, -1.0f);
|
collisionNormal = new Vector2(0.0f, -1.0f);
|
||||||
}
|
}
|
||||||
@@ -378,11 +390,11 @@ namespace Barotrauma.Particles
|
|||||||
}
|
}
|
||||||
|
|
||||||
collisionNormal = Vector2.Zero;
|
collisionNormal = Vector2.Zero;
|
||||||
if (velocity.X < 0.0f && position.X - prefab.CollisionRadius * size.X < hullRect.X)
|
if (velocity.X < 0.0f && position.X - colliderRadius.X < hullRect.X)
|
||||||
{
|
{
|
||||||
collisionNormal = new Vector2(1.0f, 0.0f);
|
collisionNormal = new Vector2(1.0f, 0.0f);
|
||||||
}
|
}
|
||||||
else if (velocity.X > 0.0f && position.X + prefab.CollisionRadius * size.X > hullRect.Right)
|
else if (velocity.X > 0.0f && position.X + colliderRadius.X > hullRect.Right)
|
||||||
{
|
{
|
||||||
collisionNormal = new Vector2(-1.0f, 0.0f);
|
collisionNormal = new Vector2(-1.0f, 0.0f);
|
||||||
}
|
}
|
||||||
@@ -431,6 +443,13 @@ namespace Barotrauma.Particles
|
|||||||
return UpdateResult.Normal;
|
return UpdateResult.Normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RefreshColliderSize()
|
||||||
|
{
|
||||||
|
if (!prefab.UseCollision) { return; }
|
||||||
|
colliderRadius = new Vector2(prefab.CollisionRadius);
|
||||||
|
if (!prefab.InvariantCollisionSize) { colliderRadius *= size; }
|
||||||
|
}
|
||||||
|
|
||||||
private void ApplyDrag(float dragCoefficient, float deltaTime)
|
private void ApplyDrag(float dragCoefficient, float deltaTime)
|
||||||
{
|
{
|
||||||
Vector2 relativeVel = velocity;
|
Vector2 relativeVel = velocity;
|
||||||
@@ -475,11 +494,11 @@ namespace Barotrauma.Particles
|
|||||||
{
|
{
|
||||||
if (collisionNormal.X > 0.0f)
|
if (collisionNormal.X > 0.0f)
|
||||||
{
|
{
|
||||||
position.X = Math.Max(position.X, prevHullRect.X + prefab.CollisionRadius * size.X);
|
position.X = Math.Max(position.X, prevHullRect.X + colliderRadius.X);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
position.X = Math.Min(position.X, prevHullRect.Right - prefab.CollisionRadius * size.X);
|
position.X = Math.Min(position.X, prevHullRect.Right - colliderRadius.X);
|
||||||
}
|
}
|
||||||
velocity.X = Math.Sign(collisionNormal.X) * Math.Abs(velocity.X) * prefab.Restitution;
|
velocity.X = Math.Sign(collisionNormal.X) * Math.Abs(velocity.X) * prefab.Restitution;
|
||||||
velocity.Y *= (1.0f - prefab.Friction);
|
velocity.Y *= (1.0f - prefab.Friction);
|
||||||
@@ -488,11 +507,11 @@ namespace Barotrauma.Particles
|
|||||||
{
|
{
|
||||||
if (collisionNormal.Y > 0.0f)
|
if (collisionNormal.Y > 0.0f)
|
||||||
{
|
{
|
||||||
position.Y = Math.Max(position.Y, prevHullRect.Y - prevHullRect.Height + prefab.CollisionRadius * size.Y);
|
position.Y = Math.Max(position.Y, prevHullRect.Y - prevHullRect.Height + colliderRadius.Y);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
position.Y = Math.Min(position.Y, prevHullRect.Y - prefab.CollisionRadius * size.Y);
|
position.Y = Math.Min(position.Y, prevHullRect.Y - colliderRadius.Y);
|
||||||
|
|
||||||
}
|
}
|
||||||
velocity.X *= (1.0f - prefab.Friction);
|
velocity.X *= (1.0f - prefab.Friction);
|
||||||
@@ -513,26 +532,26 @@ namespace Barotrauma.Particles
|
|||||||
|
|
||||||
if (position.Y < center.Y)
|
if (position.Y < center.Y)
|
||||||
{
|
{
|
||||||
position.Y = hullRect.Y - hullRect.Height - prefab.CollisionRadius;
|
position.Y = hullRect.Y - hullRect.Height - colliderRadius.Y;
|
||||||
velocity.X *= (1.0f - prefab.Friction);
|
velocity.X *= (1.0f - prefab.Friction);
|
||||||
velocity.Y = -velocity.Y * prefab.Restitution;
|
velocity.Y = -velocity.Y * prefab.Restitution;
|
||||||
}
|
}
|
||||||
else if (position.Y > center.Y)
|
else if (position.Y > center.Y)
|
||||||
{
|
{
|
||||||
position.Y = hullRect.Y + prefab.CollisionRadius;
|
position.Y = hullRect.Y + colliderRadius.Y;
|
||||||
velocity.X *= (1.0f - prefab.Friction);
|
velocity.X *= (1.0f - prefab.Friction);
|
||||||
velocity.Y = -velocity.Y * prefab.Restitution;
|
velocity.Y = -velocity.Y * prefab.Restitution;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (position.X < center.X)
|
if (position.X < center.X)
|
||||||
{
|
{
|
||||||
position.X = hullRect.X - prefab.CollisionRadius;
|
position.X = hullRect.X - colliderRadius.X;
|
||||||
velocity.X = -velocity.X * prefab.Restitution;
|
velocity.X = -velocity.X * prefab.Restitution;
|
||||||
velocity.Y *= (1.0f - prefab.Friction);
|
velocity.Y *= (1.0f - prefab.Friction);
|
||||||
}
|
}
|
||||||
else if (position.X > center.X)
|
else if (position.X > center.X)
|
||||||
{
|
{
|
||||||
position.X = hullRect.X + hullRect.Width + prefab.CollisionRadius;
|
position.X = hullRect.X + hullRect.Width + colliderRadius.X;
|
||||||
velocity.X = -velocity.X * prefab.Restitution;
|
velocity.X = -velocity.X * prefab.Restitution;
|
||||||
velocity.Y *= (1.0f - prefab.Friction);
|
velocity.Y *= (1.0f - prefab.Friction);
|
||||||
}
|
}
|
||||||
@@ -559,11 +578,12 @@ namespace Barotrauma.Particles
|
|||||||
|
|
||||||
Color currColor = new Color(color.ToVector4() * ColorMultiplier);
|
Color currColor = new Color(color.ToVector4() * ColorMultiplier);
|
||||||
|
|
||||||
if (prefab.Sprites[spriteIndex] is SpriteSheet)
|
Vector2 drawPos = new Vector2(drawPosition.X, -drawPosition.Y);
|
||||||
|
if (prefab.Sprites[spriteIndex] is SpriteSheet sheet)
|
||||||
{
|
{
|
||||||
((SpriteSheet)prefab.Sprites[spriteIndex]).Draw(
|
sheet.Draw(
|
||||||
spriteBatch, animFrame,
|
spriteBatch, animFrame,
|
||||||
new Vector2(drawPosition.X, -drawPosition.Y),
|
drawPos,
|
||||||
currColor * (currColor.A / 255.0f),
|
currColor * (currColor.A / 255.0f),
|
||||||
prefab.Sprites[spriteIndex].Origin, drawRotation,
|
prefab.Sprites[spriteIndex].Origin, drawRotation,
|
||||||
drawSize, SpriteEffects.None, prefab.Sprites[spriteIndex].Depth);
|
drawSize, SpriteEffects.None, prefab.Sprites[spriteIndex].Depth);
|
||||||
@@ -571,11 +591,23 @@ namespace Barotrauma.Particles
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
prefab.Sprites[spriteIndex].Draw(spriteBatch,
|
prefab.Sprites[spriteIndex].Draw(spriteBatch,
|
||||||
new Vector2(drawPosition.X, -drawPosition.Y),
|
drawPos,
|
||||||
currColor * (currColor.A / 255.0f),
|
currColor * (currColor.A / 255.0f),
|
||||||
prefab.Sprites[spriteIndex].Origin, drawRotation,
|
prefab.Sprites[spriteIndex].Origin, drawRotation,
|
||||||
drawSize, SpriteEffects.None, prefab.Sprites[spriteIndex].Depth);
|
drawSize, SpriteEffects.None, prefab.Sprites[spriteIndex].Depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*if (GameMain.DebugDraw && prefab.UseCollision)
|
||||||
|
{
|
||||||
|
GUI.DrawLine(spriteBatch,
|
||||||
|
drawPos - Vector2.UnitX * colliderRadius.X,
|
||||||
|
drawPos + Vector2.UnitX * colliderRadius.X,
|
||||||
|
Color.Gray);
|
||||||
|
GUI.DrawLine(spriteBatch,
|
||||||
|
drawPos - Vector2.UnitY * colliderRadius.Y,
|
||||||
|
drawPos + Vector2.UnitY * colliderRadius.Y,
|
||||||
|
Color.Gray);
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,6 +135,9 @@ namespace Barotrauma.Particles
|
|||||||
[Editable(0.0f, 10000.0f), Serialize(0.0f, IsPropertySaveable.No, description: "Radius of the particle's collider. Only has an effect if UseCollision is set to true.")]
|
[Editable(0.0f, 10000.0f), Serialize(0.0f, IsPropertySaveable.No, description: "Radius of the particle's collider. Only has an effect if UseCollision is set to true.")]
|
||||||
public float CollisionRadius { get; private set; }
|
public float CollisionRadius { get; private set; }
|
||||||
|
|
||||||
|
[Editable, Serialize(false, IsPropertySaveable.No, description: "If enabled, the size (or changes in size) of the particle doesn't affect the size of the collider.")]
|
||||||
|
public bool InvariantCollisionSize { get; private set; }
|
||||||
|
|
||||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Does the particle collide with the walls of the submarine and the level.")]
|
[Editable, Serialize(false, IsPropertySaveable.No, description: "Does the particle collide with the walls of the submarine and the level.")]
|
||||||
public bool UseCollision { get; private set; }
|
public bool UseCollision { get; private set; }
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Barotrauma
|
|||||||
private RenderTarget2D renderTargetWater;
|
private RenderTarget2D renderTargetWater;
|
||||||
private RenderTarget2D renderTargetFinal;
|
private RenderTarget2D renderTargetFinal;
|
||||||
|
|
||||||
private readonly Effect damageEffect;
|
public readonly Effect DamageEffect;
|
||||||
private readonly Texture2D damageStencil;
|
private readonly Texture2D damageStencil;
|
||||||
private readonly Texture2D distortTexture;
|
private readonly Texture2D distortTexture;
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ namespace Barotrauma
|
|||||||
};
|
};
|
||||||
|
|
||||||
//var blurEffect = LoadEffect("Effects/blurshader");
|
//var blurEffect = LoadEffect("Effects/blurshader");
|
||||||
damageEffect = EffectLoader.Load("Effects/damageshader");
|
DamageEffect = EffectLoader.Load("Effects/damageshader");
|
||||||
PostProcessEffect = EffectLoader.Load("Effects/postprocess");
|
PostProcessEffect = EffectLoader.Load("Effects/postprocess");
|
||||||
GradientEffect = EffectLoader.Load("Effects/gradientshader");
|
GradientEffect = EffectLoader.Load("Effects/gradientshader");
|
||||||
GrainEffect = EffectLoader.Load("Effects/grainshader");
|
GrainEffect = EffectLoader.Load("Effects/grainshader");
|
||||||
@@ -47,9 +47,9 @@ namespace Barotrauma
|
|||||||
BlueprintEffect = EffectLoader.Load("Effects/blueprintshader");
|
BlueprintEffect = EffectLoader.Load("Effects/blueprintshader");
|
||||||
|
|
||||||
damageStencil = TextureLoader.FromFile("Content/Map/walldamage.png");
|
damageStencil = TextureLoader.FromFile("Content/Map/walldamage.png");
|
||||||
damageEffect.Parameters["xStencil"].SetValue(damageStencil);
|
DamageEffect.Parameters["xStencil"].SetValue(damageStencil);
|
||||||
damageEffect.Parameters["aMultiplier"].SetValue(50.0f);
|
DamageEffect.Parameters["aMultiplier"].SetValue(50.0f);
|
||||||
damageEffect.Parameters["cMultiplier"].SetValue(200.0f);
|
DamageEffect.Parameters["cMultiplier"].SetValue(200.0f);
|
||||||
|
|
||||||
distortTexture = TextureLoader.FromFile("Content/Effects/distortnormals.png");
|
distortTexture = TextureLoader.FromFile("Content/Effects/distortnormals.png");
|
||||||
PostProcessEffect.Parameters["xDistortTexture"].SetValue(distortTexture);
|
PostProcessEffect.Parameters["xDistortTexture"].SetValue(distortTexture);
|
||||||
@@ -346,12 +346,13 @@ namespace Barotrauma
|
|||||||
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:FrontParticles", sw.ElapsedTicks);
|
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:FrontParticles", sw.ElapsedTicks);
|
||||||
sw.Restart();
|
sw.Restart();
|
||||||
|
|
||||||
|
DamageEffect.CurrentTechnique = DamageEffect.Techniques["StencilShader"];
|
||||||
spriteBatch.Begin(SpriteSortMode.Immediate,
|
spriteBatch.Begin(SpriteSortMode.Immediate,
|
||||||
BlendState.NonPremultiplied, SamplerState.LinearWrap,
|
BlendState.NonPremultiplied, SamplerState.LinearWrap,
|
||||||
null, null,
|
null, null,
|
||||||
damageEffect,
|
DamageEffect,
|
||||||
cam.Transform);
|
cam.Transform);
|
||||||
Submarine.DrawDamageable(spriteBatch, damageEffect, false);
|
Submarine.DrawDamageable(spriteBatch, DamageEffect, false);
|
||||||
spriteBatch.End();
|
spriteBatch.End();
|
||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
@@ -378,7 +379,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
graphics.DepthStencilState = DepthStencilState.None;
|
graphics.DepthStencilState = DepthStencilState.None;
|
||||||
graphics.SamplerStates[0] = SamplerState.LinearWrap;
|
graphics.SamplerStates[0] = SamplerState.LinearWrap;
|
||||||
graphics.BlendState = Lights.CustomBlendStates.Multiplicative;
|
graphics.BlendState = CustomBlendStates.Multiplicative;
|
||||||
Quad.UseBasicEffect(GameMain.LightManager.LightMap);
|
Quad.UseBasicEffect(GameMain.LightManager.LightMap);
|
||||||
Quad.Render();
|
Quad.Render();
|
||||||
}
|
}
|
||||||
@@ -409,6 +410,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
GameMain.LightManager.LosEffect.CurrentTechnique = GameMain.LightManager.LosEffect.Techniques["LosShader"];
|
GameMain.LightManager.LosEffect.CurrentTechnique = GameMain.LightManager.LosEffect.Techniques["LosShader"];
|
||||||
|
|
||||||
|
GameMain.LightManager.LosEffect.Parameters["blurDistance"].SetValue(0.005f);
|
||||||
GameMain.LightManager.LosEffect.Parameters["xTexture"].SetValue(renderTargetBackground);
|
GameMain.LightManager.LosEffect.Parameters["xTexture"].SetValue(renderTargetBackground);
|
||||||
GameMain.LightManager.LosEffect.Parameters["xLosTexture"].SetValue(GameMain.LightManager.LosTexture);
|
GameMain.LightManager.LosEffect.Parameters["xLosTexture"].SetValue(GameMain.LightManager.LosTexture);
|
||||||
GameMain.LightManager.LosEffect.Parameters["xLosAlpha"].SetValue(GameMain.LightManager.LosAlpha);
|
GameMain.LightManager.LosEffect.Parameters["xLosAlpha"].SetValue(GameMain.LightManager.LosAlpha);
|
||||||
@@ -434,8 +436,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
graphics.BlendState = BlendState.NonPremultiplied;
|
graphics.BlendState = BlendState.NonPremultiplied;
|
||||||
graphics.SamplerStates[0] = SamplerState.PointClamp;
|
graphics.SamplerStates[0] = SamplerState.PointClamp;
|
||||||
|
graphics.SamplerStates[1] = SamplerState.PointClamp;
|
||||||
GameMain.LightManager.LosEffect.CurrentTechnique.Passes[0].Apply();
|
GameMain.LightManager.LosEffect.CurrentTechnique.Passes[0].Apply();
|
||||||
Quad.Render();
|
Quad.Render();
|
||||||
|
graphics.SamplerStates[0] = SamplerState.LinearWrap;
|
||||||
|
graphics.SamplerStates[1] = SamplerState.LinearWrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Character.Controlled is { } character)
|
if (Character.Controlled is { } character)
|
||||||
@@ -519,6 +524,11 @@ namespace Barotrauma
|
|||||||
spriteBatch.End();
|
spriteBatch.End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GameMain.LightManager.DebugLos)
|
||||||
|
{
|
||||||
|
GameMain.LightManager.DebugDrawLos(spriteBatch, cam);
|
||||||
|
}
|
||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:PostProcess", sw.ElapsedTicks);
|
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:PostProcess", sw.ElapsedTicks);
|
||||||
sw.Restart();
|
sw.Restart();
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ namespace Barotrauma
|
|||||||
private GUIImage playstyleBanner;
|
private GUIImage playstyleBanner;
|
||||||
private GUITextBlock playstyleDescription;
|
private GUITextBlock playstyleDescription;
|
||||||
|
|
||||||
private const string RemoteContentUrl = "http://www.barotraumagame.com/gamedata/";
|
|
||||||
private readonly GUIComponent remoteContentContainer;
|
private readonly GUIComponent remoteContentContainer;
|
||||||
private XDocument remoteContentDoc;
|
private XDocument remoteContentDoc;
|
||||||
|
|
||||||
@@ -82,6 +81,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
GameMain.Instance.ResolutionChanged += () =>
|
GameMain.Instance.ResolutionChanged += () =>
|
||||||
{
|
{
|
||||||
|
SetMenuTabPositioning();
|
||||||
CreateHostServerFields();
|
CreateHostServerFields();
|
||||||
CreateCampaignSetupUI();
|
CreateCampaignSetupUI();
|
||||||
SettingsMenu.Create(menuTabs[Tab.Settings].RectTransform);
|
SettingsMenu.Create(menuTabs[Tab.Settings].RectTransform);
|
||||||
@@ -444,31 +444,33 @@ namespace Barotrauma
|
|||||||
var relativeSize = new Vector2(0.6f, 0.65f);
|
var relativeSize = new Vector2(0.6f, 0.65f);
|
||||||
var minSize = new Point(600, 400);
|
var minSize = new Point(600, 400);
|
||||||
var maxSize = new Point(2000, 1500);
|
var maxSize = new Point(2000, 1500);
|
||||||
var anchor = Anchor.CenterRight;
|
var anchor = Anchor.Center;
|
||||||
var pivot = Pivot.CenterRight;
|
var pivot = Pivot.Center;
|
||||||
Vector2 relativeSpacing = new Vector2(0.05f, 0.0f);
|
Vector2 relativeOffset = new Vector2(0.05f, 0.0f);
|
||||||
|
|
||||||
menuTabs = new Dictionary<Tab, GUIFrame>();
|
menuTabs = new Dictionary<Tab, GUIFrame>
|
||||||
|
{
|
||||||
menuTabs[Tab.Settings] = new GUIFrame(new RectTransform(new Vector2(relativeSize.X, 0.8f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing },
|
[Tab.Settings] = new GUIFrame(new RectTransform(new Vector2(relativeSize.X, 0.8f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset },
|
||||||
style: null);
|
style: null)
|
||||||
menuTabs[Tab.Settings].CanBeFocused = false;
|
{
|
||||||
|
CanBeFocused = false
|
||||||
menuTabs[Tab.NewGame] = new GUIFrame(new RectTransform(relativeSize * new Vector2(1.0f, 1.15f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
|
},
|
||||||
menuTabs[Tab.LoadGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
|
[Tab.NewGame] = new GUIFrame(new RectTransform(relativeSize * new Vector2(1.0f, 1.15f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset }),
|
||||||
|
[Tab.LoadGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset })
|
||||||
|
};
|
||||||
|
|
||||||
CreateCampaignSetupUI();
|
CreateCampaignSetupUI();
|
||||||
|
|
||||||
var hostServerScale = new Vector2(0.7f, 1.2f);
|
var hostServerScale = new Vector2(0.7f, 1.2f);
|
||||||
menuTabs[Tab.HostServer] = new GUIFrame(new RectTransform(
|
menuTabs[Tab.HostServer] = new GUIFrame(new RectTransform(
|
||||||
Vector2.Multiply(relativeSize, hostServerScale), GUI.Canvas, anchor, pivot, minSize.Multiply(hostServerScale), maxSize.Multiply(hostServerScale))
|
Vector2.Multiply(relativeSize, hostServerScale), GUI.Canvas, anchor, pivot, minSize.Multiply(hostServerScale), maxSize.Multiply(hostServerScale))
|
||||||
{ RelativeOffset = relativeSpacing });
|
{ RelativeOffset = relativeOffset });
|
||||||
|
|
||||||
CreateHostServerFields();
|
CreateHostServerFields();
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
menuTabs[Tab.Tutorials] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
|
menuTabs[Tab.Tutorials] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset });
|
||||||
CreateTutorialTab();
|
CreateTutorialTab();
|
||||||
|
|
||||||
this.game = game;
|
this.game = game;
|
||||||
@@ -489,6 +491,20 @@ namespace Barotrauma
|
|||||||
SelectTab(Tab.Empty);
|
SelectTab(Tab.Empty);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SetMenuTabPositioning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetMenuTabPositioning()
|
||||||
|
{
|
||||||
|
foreach (GUIFrame menuTab in menuTabs.Values)
|
||||||
|
{
|
||||||
|
var anchor = GUI.IsUltrawide ? Anchor.Center : Anchor.CenterRight;
|
||||||
|
var pivot = GUI.IsUltrawide ? Pivot.Center : Pivot.CenterRight;
|
||||||
|
Vector2 relativeOffset = GUI.IsUltrawide ? Vector2.Zero : new Vector2(0.05f, 0.0f);
|
||||||
|
menuTab.RectTransform.SetPosition(anchor, pivot);
|
||||||
|
menuTab.RectTransform.RelativeOffset = relativeOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateTutorialTab()
|
private void CreateTutorialTab()
|
||||||
@@ -1532,10 +1548,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void FetchRemoteContent()
|
private void FetchRemoteContent()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(RemoteContentUrl)) { return; }
|
string remoteContentUrl = GameSettings.CurrentConfig.RemoteMainMenuContentUrl;
|
||||||
|
if (string.IsNullOrEmpty(remoteContentUrl)) { return; }
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var client = new RestClient(RemoteContentUrl);
|
var client = new RestClient(remoteContentUrl);
|
||||||
var request = new RestRequest("MenuContent.xml", Method.GET);
|
var request = new RestRequest("MenuContent.xml", Method.GET);
|
||||||
TaskPool.Add("RequestMainMenuRemoteContent", client.ExecuteAsync(request),
|
TaskPool.Add("RequestMainMenuRemoteContent", client.ExecuteAsync(request),
|
||||||
RemoteContentReceived);
|
RemoteContentReceived);
|
||||||
|
|||||||
@@ -751,12 +751,20 @@ namespace Barotrauma
|
|||||||
};
|
};
|
||||||
ServerMessage.OnTextChanged += (textBox, text) =>
|
ServerMessage.OnTextChanged += (textBox, text) =>
|
||||||
{
|
{
|
||||||
Vector2 textSize = textBox.Font.MeasureString(textBox.WrappedText);
|
|
||||||
textBox.RectTransform.NonScaledSize = new Point(textBox.RectTransform.NonScaledSize.X, Math.Max(serverMessageContainer.Content.Rect.Height, (int)textSize.Y + 10));
|
|
||||||
serverMessageContainer.UpdateScrollBarSize();
|
|
||||||
serverMessageHint.Visible = !textBox.Selected && !textBox.Readonly && string.IsNullOrWhiteSpace(textBox.Text);
|
serverMessageHint.Visible = !textBox.Selected && !textBox.Readonly && string.IsNullOrWhiteSpace(textBox.Text);
|
||||||
|
RefreshServerInfoSize();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
ServerMessage.RectTransform.SizeChanged += RefreshServerInfoSize;
|
||||||
|
|
||||||
|
void RefreshServerInfoSize()
|
||||||
|
{
|
||||||
|
serverMessageHint.Visible = !ServerMessage.Selected && !ServerMessage.Readonly && string.IsNullOrWhiteSpace(ServerMessage.Text);
|
||||||
|
Vector2 textSize = ServerMessage.Font.MeasureString(ServerMessage.WrappedText);
|
||||||
|
ServerMessage.RectTransform.NonScaledSize = new Point(ServerMessage.RectTransform.NonScaledSize.X, Math.Max(serverMessageContainer.Content.Rect.Height, (int)textSize.Y + 10));
|
||||||
|
serverMessageContainer.UpdateScrollBarSize();
|
||||||
|
}
|
||||||
|
|
||||||
ServerMessage.OnEnterPressed += (textBox, text) =>
|
ServerMessage.OnEnterPressed += (textBox, text) =>
|
||||||
{
|
{
|
||||||
string str = textBox.Text;
|
string str = textBox.Text;
|
||||||
|
|||||||
+54
-15
@@ -812,7 +812,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private bool SortList(GUIButton button, object obj)
|
private bool SortList(GUIButton button, object obj)
|
||||||
{
|
{
|
||||||
if (!(obj is ColumnLabel sortBy)) { return false; }
|
if (obj is not ColumnLabel sortBy) { return false; }
|
||||||
SortList(sortBy, toggle: true);
|
SortList(sortBy, toggle: true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -848,8 +848,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (c1.GUIComponent.UserData is not ServerInfo s1) { return 0; }
|
if (c1.GUIComponent.UserData is not ServerInfo s1) { return 0; }
|
||||||
if (c2.GUIComponent.UserData is not ServerInfo s2) { return 0; }
|
if (c2.GUIComponent.UserData is not ServerInfo s2) { return 0; }
|
||||||
int comparison = sortedAscending ? 1 : -1;
|
return CompareServer(sortBy, s1, s2, sortedAscending);
|
||||||
return CompareServer(sortBy, s1, s2) * comparison;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -857,22 +856,31 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
var children = serverList.Content.RectTransform.Children.Reverse().ToList();
|
var children = serverList.Content.RectTransform.Children.Reverse().ToList();
|
||||||
|
|
||||||
int comparison = sortedAscending ? 1 : -1;
|
|
||||||
foreach (var child in children)
|
foreach (var child in children)
|
||||||
{
|
{
|
||||||
if (child.GUIComponent.UserData is not ServerInfo serverInfo2 || serverInfo.Equals(serverInfo2)) { continue; }
|
if (child.GUIComponent.UserData is not ServerInfo serverInfo2 || serverInfo.Equals(serverInfo2)) { continue; }
|
||||||
if (CompareServer(sortedBy, serverInfo, serverInfo2) * comparison >= 0)
|
if (CompareServer(sortedBy, serverInfo, serverInfo2, sortedAscending) >= 0)
|
||||||
{
|
{
|
||||||
var index = serverList.Content.RectTransform.GetChildIndex(child);
|
var index = serverList.Content.RectTransform.GetChildIndex(child);
|
||||||
component.RectTransform.RepositionChildInHierarchy(index + 1);
|
component.RectTransform.RepositionChildInHierarchy(Math.Min(index + 1, serverList.Content.CountChildren - 1));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
component.RectTransform.SetAsFirstChild();
|
component.RectTransform.SetAsFirstChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int CompareServer(ColumnLabel sortBy, ServerInfo s1, ServerInfo s2)
|
private static int CompareServer(ColumnLabel sortBy, ServerInfo s1, ServerInfo s2, bool ascending)
|
||||||
{
|
{
|
||||||
|
//always put servers with unknown ping at the bottom (unless we're specifically sorting by ping)
|
||||||
|
//servers without a ping are often unreachable/spam
|
||||||
|
bool s1HasPing = s1.Ping.IsSome();
|
||||||
|
bool s2HasPing = s2.Ping.IsSome();
|
||||||
|
if (s1HasPing != s2HasPing)
|
||||||
|
{
|
||||||
|
return s1HasPing ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int comparison = ascending ? 1 : -1;
|
||||||
switch (sortBy)
|
switch (sortBy)
|
||||||
{
|
{
|
||||||
case ColumnLabel.ServerListCompatible:
|
case ColumnLabel.ServerListCompatible:
|
||||||
@@ -880,18 +888,18 @@ namespace Barotrauma
|
|||||||
bool s2Compatible = NetworkMember.IsCompatible(GameMain.Version, s2.GameVersion);
|
bool s2Compatible = NetworkMember.IsCompatible(GameMain.Version, s2.GameVersion);
|
||||||
|
|
||||||
if (s1Compatible == s2Compatible) { return 0; }
|
if (s1Compatible == s2Compatible) { return 0; }
|
||||||
return s1Compatible ? -1 : 1;
|
return (s1Compatible ? -1 : 1) * comparison;
|
||||||
case ColumnLabel.ServerListHasPassword:
|
case ColumnLabel.ServerListHasPassword:
|
||||||
if (s1.HasPassword == s2.HasPassword) { return 0; }
|
if (s1.HasPassword == s2.HasPassword) { return 0; }
|
||||||
return s1.HasPassword ? 1 : -1;
|
return (s1.HasPassword ? 1 : -1) * comparison;
|
||||||
case ColumnLabel.ServerListName:
|
case ColumnLabel.ServerListName:
|
||||||
// I think we actually want culture-specific sorting here?
|
// I think we actually want culture-specific sorting here?
|
||||||
return string.Compare(s1.ServerName, s2.ServerName, StringComparison.CurrentCulture);
|
return string.Compare(s1.ServerName, s2.ServerName, StringComparison.CurrentCulture) * comparison;
|
||||||
case ColumnLabel.ServerListRoundStarted:
|
case ColumnLabel.ServerListRoundStarted:
|
||||||
if (s1.GameStarted == s2.GameStarted) { return 0; }
|
if (s1.GameStarted == s2.GameStarted) { return 0; }
|
||||||
return s1.GameStarted ? 1 : -1;
|
return (s1.GameStarted ? 1 : -1) * comparison;
|
||||||
case ColumnLabel.ServerListPlayers:
|
case ColumnLabel.ServerListPlayers:
|
||||||
return s2.PlayerCount.CompareTo(s1.PlayerCount);
|
return s2.PlayerCount.CompareTo(s1.PlayerCount) * comparison;
|
||||||
case ColumnLabel.ServerListPing:
|
case ColumnLabel.ServerListPing:
|
||||||
return (s1.Ping.TryUnwrap(out var s1Ping), s2.Ping.TryUnwrap(out var s2Ping)) switch
|
return (s1.Ping.TryUnwrap(out var s1Ping), s2.Ping.TryUnwrap(out var s2Ping)) switch
|
||||||
{
|
{
|
||||||
@@ -899,7 +907,7 @@ namespace Barotrauma
|
|||||||
(true, true) => s2Ping.CompareTo(s1Ping),
|
(true, true) => s2Ping.CompareTo(s1Ping),
|
||||||
(false, true) => 1,
|
(false, true) => 1,
|
||||||
(true, false) => -1
|
(true, false) => -1
|
||||||
};
|
} * comparison;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1504,9 +1512,41 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void AddToServerList(ServerInfo serverInfo, bool skipPing = false)
|
private void AddToServerList(ServerInfo serverInfo, bool skipPing = false)
|
||||||
{
|
{
|
||||||
|
const int MaxAllowedPlayers = 1000;
|
||||||
|
const int MaxAllowedSimilarServers = 10;
|
||||||
|
const float MinSimilarityPercentage = 0.8f;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(serverInfo.ServerName)) { return; }
|
||||||
if (serverInfo.PlayerCount > serverInfo.MaxPlayers) { return; }
|
if (serverInfo.PlayerCount > serverInfo.MaxPlayers) { return; }
|
||||||
if (serverInfo.PlayerCount < 0) { return; }
|
if (serverInfo.PlayerCount < 0) { return; }
|
||||||
if (serverInfo.MaxPlayers <= 0) { return; }
|
if (serverInfo.MaxPlayers <= 0) { return; }
|
||||||
|
//no way a legit server can have this many players
|
||||||
|
if (serverInfo.MaxPlayers > MaxAllowedPlayers) { return; }
|
||||||
|
|
||||||
|
int similarServerCount = 0;
|
||||||
|
string serverInfoStr = getServerInfoStr(serverInfo);
|
||||||
|
foreach (var serverElement in serverList.Content.Children)
|
||||||
|
{
|
||||||
|
if (!serverElement.Visible) { continue; }
|
||||||
|
if (serverElement.UserData is not ServerInfo otherServer || otherServer == serverInfo) { continue; }
|
||||||
|
if (ToolBox.LevenshteinDistance(serverInfoStr, getServerInfoStr(otherServer)) < serverInfoStr.Length * (1.0f - MinSimilarityPercentage))
|
||||||
|
{
|
||||||
|
similarServerCount++;
|
||||||
|
if (similarServerCount > MaxAllowedSimilarServers)
|
||||||
|
{
|
||||||
|
DebugConsole.Log($"Server {serverInfo.ServerName} seems to be almost identical to {otherServer.ServerName}. Hiding as a potential spam server.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (similarServerCount > MaxAllowedSimilarServers) { return; }
|
||||||
|
|
||||||
|
static string getServerInfoStr(ServerInfo serverInfo)
|
||||||
|
{
|
||||||
|
string str = serverInfo.ServerName + serverInfo.ServerMessage + serverInfo.MaxPlayers;
|
||||||
|
if (str.Length > 200) { return str.Substring(0, 200); }
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
RemoveMsgFromServerList(MsgUserData.RefreshingServerList);
|
RemoveMsgFromServerList(MsgUserData.RefreshingServerList);
|
||||||
RemoveMsgFromServerList(MsgUserData.NoServers);
|
RemoveMsgFromServerList(MsgUserData.NoServers);
|
||||||
@@ -1522,7 +1562,6 @@ namespace Barotrauma
|
|||||||
UpdateServerInfoUI(serverInfo);
|
UpdateServerInfoUI(serverInfo);
|
||||||
if (!skipPing) { PingUtils.GetServerPing(serverInfo, UpdateServerInfoUI); }
|
if (!skipPing) { PingUtils.GetServerPing(serverInfo, UpdateServerInfoUI); }
|
||||||
|
|
||||||
InsertServer(serverInfo, serverFrame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateServerInfoUI(ServerInfo serverInfo)
|
private void UpdateServerInfoUI(ServerInfo serverInfo)
|
||||||
@@ -1736,7 +1775,7 @@ namespace Barotrauma
|
|||||||
AddToFavoriteServers(serverInfo);
|
AddToFavoriteServers(serverInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
SortList(sortedBy, toggle: false);
|
InsertServer(serverInfo, serverFrame);
|
||||||
FilterServers();
|
FilterServers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -525,6 +525,11 @@ namespace Barotrauma
|
|||||||
GUI.AddMessage(TextManager.Get("waypointsgeneratedsuccesfully"), GUIStyle.Green);
|
GUI.AddMessage(TextManager.Get("waypointsgeneratedsuccesfully"), GUIStyle.Green);
|
||||||
}
|
}
|
||||||
WayPoint.ShowWayPoints = true;
|
WayPoint.ShowWayPoints = true;
|
||||||
|
var matchingTickBox = showEntitiesTickBoxes?.Find(tb => tb.UserData as string == "waypoint");
|
||||||
|
if (matchingTickBox != null)
|
||||||
|
{
|
||||||
|
matchingTickBox.Selected = true;
|
||||||
|
}
|
||||||
generateWaypointsVerification.Close();
|
generateWaypointsVerification.Close();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -2850,7 +2855,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
OnClicked = (button, o) =>
|
OnClicked = (button, o) =>
|
||||||
{
|
{
|
||||||
var requiredPackages = MapEntity.mapEntityList.Select(e => e.Prefab.ContentPackage)
|
var requiredPackages = MapEntity.mapEntityList.Select(e => e?.Prefab?.ContentPackage)
|
||||||
.Where(cp => cp != null)
|
.Where(cp => cp != null)
|
||||||
.Distinct().OfType<ContentPackage>().Select(p => p.Name).ToHashSet();
|
.Distinct().OfType<ContentPackage>().Select(p => p.Name).ToHashSet();
|
||||||
var tickboxes = requiredContentPackList.Content.Children.OfType<GUITickBox>().ToArray();
|
var tickboxes = requiredContentPackList.Content.Children.OfType<GUITickBox>().ToArray();
|
||||||
@@ -5794,7 +5799,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
item.SetTransform(dummyCharacter.SimPosition, 0.0f);
|
item.SetTransform(dummyCharacter.SimPosition, 0.0f);
|
||||||
item.UpdateTransform();
|
item.UpdateTransform();
|
||||||
item.SetTransform(item.body.SimPosition, 0.0f);
|
if (item.body != null)
|
||||||
|
{
|
||||||
|
item.SetTransform(item.body.SimPosition, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
//wires need to be updated for the last node to follow the player during rewiring
|
//wires need to be updated for the last node to follow the player during rewiring
|
||||||
Wire wire = item.GetComponent<Wire>();
|
Wire wire = item.GetComponent<Wire>();
|
||||||
@@ -5907,6 +5915,11 @@ namespace Barotrauma
|
|||||||
spriteBatch.End();
|
spriteBatch.End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GameMain.LightManager.DebugLos)
|
||||||
|
{
|
||||||
|
GameMain.LightManager.DebugDrawLos(spriteBatch, cam);
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------- HUD -----------------------------
|
//-------------------- HUD -----------------------------
|
||||||
|
|
||||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState);
|
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState);
|
||||||
|
|||||||
@@ -347,8 +347,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
Identifier propertyTag = $"{property.PropertyInfo.DeclaringType.Name}.{property.PropertyInfo.Name}".ToIdentifier();
|
Identifier propertyTag = $"{property.PropertyInfo.DeclaringType.Name}.{property.PropertyInfo.Name}".ToIdentifier();
|
||||||
Identifier fallbackTag = property.PropertyInfo.Name.ToIdentifier();
|
Identifier fallbackTag = property.PropertyInfo.Name.ToIdentifier();
|
||||||
LocalizedString displayName =
|
LocalizedString displayName = TextManager.Get(propertyTag, $"sp.{propertyTag}.name".ToIdentifier());
|
||||||
TextManager.Get(propertyTag, $"sp.{propertyTag}.name".ToIdentifier());
|
|
||||||
if (displayName.IsNullOrEmpty())
|
if (displayName.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
Editable editable = property.GetAttribute<Editable>();
|
Editable editable = property.GetAttribute<Editable>();
|
||||||
@@ -380,10 +379,14 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
LocalizedString toolTip = TextManager.Get($"sp.{propertyTag}.description");
|
LocalizedString toolTip = TextManager.Get($"sp.{propertyTag}.description");
|
||||||
if (toolTip.IsNullOrEmpty() && entity.GetType() != property.PropertyInfo.DeclaringType)
|
if (entity.GetType() != property.PropertyInfo.DeclaringType)
|
||||||
{
|
{
|
||||||
Identifier propertyTagForDerivedClass = $"{entity.GetType().Name}.{property.PropertyInfo.Name}".ToIdentifier();
|
Identifier propertyTagForDerivedClass = $"{entity.GetType().Name}.{property.PropertyInfo.Name}".ToIdentifier();
|
||||||
toolTip = TextManager.Get($"{propertyTagForDerivedClass}.description", $"sp.{propertyTagForDerivedClass}.description");
|
var toolTipForDerivedClass = TextManager.Get($"{propertyTagForDerivedClass}.description", $"sp.{propertyTagForDerivedClass}.description");
|
||||||
|
if (!toolTipForDerivedClass.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
toolTip = toolTipForDerivedClass;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (toolTip.IsNullOrEmpty())
|
if (toolTip.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -189,6 +189,7 @@ namespace Barotrauma.Steam
|
|||||||
ModProject modProject = new ModProject(tempPkg)
|
ModProject modProject = new ModProject(tempPkg)
|
||||||
{
|
{
|
||||||
ModVersion = modVersion,
|
ModVersion = modVersion,
|
||||||
|
Name = title,
|
||||||
ExpectedHash = tempPkg.CalculateHash(name: title, modVersion: modVersion)
|
ExpectedHash = tempPkg.CalculateHash(name: title, modVersion: modVersion)
|
||||||
};
|
};
|
||||||
modProject.Save(stagingFileListPath);
|
modProject.Save(stagingFileListPath);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Barotrauma.Extensions;
|
|||||||
using Barotrauma.IO;
|
using Barotrauma.IO;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
using Steamworks;
|
||||||
using Directory = Barotrauma.IO.Directory;
|
using Directory = Barotrauma.IO.Directory;
|
||||||
using ItemOrPackage = Barotrauma.Either<Steamworks.Ugc.Item, Barotrauma.ContentPackage>;
|
using ItemOrPackage = Barotrauma.Either<Steamworks.Ugc.Item, Barotrauma.ContentPackage>;
|
||||||
using Path = Barotrauma.IO.Path;
|
using Path = Barotrauma.IO.Path;
|
||||||
@@ -157,7 +158,7 @@ namespace Barotrauma.Steam
|
|||||||
}
|
}
|
||||||
|
|
||||||
var selectedTitle =
|
var selectedTitle =
|
||||||
new GUITextBlock(new RectTransform((1.0f, 0.05f), mainLayout.RectTransform), workshopItem.Title ?? localPackage.Name,
|
new GUITextBlock(new RectTransform((1.0f, 0.05f), mainLayout.RectTransform), localPackage.Name,
|
||||||
font: GUIStyle.LargeFont);
|
font: GUIStyle.LargeFont);
|
||||||
if (workshopItem.Id != 0)
|
if (workshopItem.Id != 0)
|
||||||
{
|
{
|
||||||
@@ -212,7 +213,7 @@ namespace Barotrauma.Steam
|
|||||||
};
|
};
|
||||||
|
|
||||||
Label(rightTop, TextManager.Get("WorkshopItemTitle"), GUIStyle.SubHeadingFont);
|
Label(rightTop, TextManager.Get("WorkshopItemTitle"), GUIStyle.SubHeadingFont);
|
||||||
var titleTextBox = new GUITextBox(NewItemRectT(rightTop), workshopItem.Title ?? localPackage.Name);
|
var titleTextBox = new GUITextBox(NewItemRectT(rightTop), localPackage.Name);
|
||||||
|
|
||||||
Label(rightTop, TextManager.Get("WorkshopItemDescription"), GUIStyle.SubHeadingFont);
|
Label(rightTop, TextManager.Get("WorkshopItemDescription"), GUIStyle.SubHeadingFont);
|
||||||
var descriptionTextBox
|
var descriptionTextBox
|
||||||
@@ -320,7 +321,9 @@ namespace Barotrauma.Steam
|
|||||||
workshopItem.Id == 0
|
workshopItem.Id == 0
|
||||||
? Steamworks.Ugc.Editor.NewCommunityFile
|
? Steamworks.Ugc.Editor.NewCommunityFile
|
||||||
: new Steamworks.Ugc.Editor(workshopItem.Id);
|
: new Steamworks.Ugc.Editor(workshopItem.Id);
|
||||||
ugcEditor = ugcEditor.WithTitle(titleTextBox.Text)
|
ugcEditor = ugcEditor
|
||||||
|
.InLanguage(SteamUtils.SteamUILanguage ?? string.Empty)
|
||||||
|
.WithTitle(titleTextBox.Text)
|
||||||
.WithDescription(descriptionTextBox.Text)
|
.WithDescription(descriptionTextBox.Text)
|
||||||
.WithTags(tagButtons.Where(kvp => kvp.Value.Selected).Select(kvp => kvp.Key.Value))
|
.WithTags(tagButtons.Where(kvp => kvp.Value.Selected).Select(kvp => kvp.Key.Value))
|
||||||
.WithChangeLog(changeNoteTextBox.Text)
|
.WithChangeLog(changeNoteTextBox.Text)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>1.0.13.2</Version>
|
<Version>1.0.20.1</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>1.0.13.2</Version>
|
<Version>1.0.20.1</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ sampler TextureSampler : register (s0) = sampler_state { Texture = <xTexture>; }
|
|||||||
Texture2D xStencil;
|
Texture2D xStencil;
|
||||||
sampler StencilSampler = sampler_state { Texture = <xStencil>; };
|
sampler StencilSampler = sampler_state { Texture = <xStencil>; };
|
||||||
|
|
||||||
|
float4 solidColor;
|
||||||
|
|
||||||
float4 inColor;
|
float4 inColor;
|
||||||
|
|
||||||
float aCutoff;
|
float aCutoff;
|
||||||
@@ -16,7 +18,6 @@ float cMultiplier;
|
|||||||
float4 main(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
float4 main(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||||
{
|
{
|
||||||
float4 c = xTexture.Sample(TextureSampler, texCoord) * inColor;
|
float4 c = xTexture.Sample(TextureSampler, texCoord) * inColor;
|
||||||
|
|
||||||
float4 stencilColor = xStencil.Sample(StencilSampler, texCoord);
|
float4 stencilColor = xStencil.Sample(StencilSampler, texCoord);
|
||||||
|
|
||||||
float aDiff = stencilColor.a - aCutoff;
|
float aDiff = stencilColor.a - aCutoff;
|
||||||
@@ -30,6 +31,18 @@ float4 main(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord
|
|||||||
min(aDiff * aMultiplier, c.a));
|
min(aDiff * aMultiplier, c.a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float4 solidColorStencil(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||||
|
{
|
||||||
|
float4 c = xTexture.Sample(TextureSampler, texCoord) * inColor;
|
||||||
|
float4 stencilColor = xStencil.Sample(StencilSampler, texCoord);
|
||||||
|
|
||||||
|
float aDiff = stencilColor.a - aCutoff;
|
||||||
|
|
||||||
|
clip(aDiff);
|
||||||
|
|
||||||
|
return float4(solidColor.rgb, solidColor.a * min(aDiff * aMultiplier, c.a));
|
||||||
|
}
|
||||||
|
|
||||||
technique StencilShader
|
technique StencilShader
|
||||||
{
|
{
|
||||||
pass Pass1
|
pass Pass1
|
||||||
@@ -37,3 +50,11 @@ technique StencilShader
|
|||||||
PixelShader = compile ps_4_0_level_9_1 main();
|
PixelShader = compile ps_4_0_level_9_1 main();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
technique StencilShaderSolidColor
|
||||||
|
{
|
||||||
|
pass Pass1
|
||||||
|
{
|
||||||
|
PixelShader = compile ps_4_0_level_9_1 solidColorStencil();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ sampler TextureSampler : register (s0) = sampler_state { Texture = <xTexture>; }
|
|||||||
Texture xStencil;
|
Texture xStencil;
|
||||||
sampler StencilSampler = sampler_state { Texture = <xStencil>; };
|
sampler StencilSampler = sampler_state { Texture = <xStencil>; };
|
||||||
|
|
||||||
|
float4 solidColor;
|
||||||
|
|
||||||
float4 inColor;
|
float4 inColor;
|
||||||
|
|
||||||
float aCutoff;
|
float aCutoff;
|
||||||
@@ -16,7 +18,6 @@ float cMultiplier;
|
|||||||
float4 main(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
float4 main(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||||
{
|
{
|
||||||
float4 c = tex2D(TextureSampler, texCoord) * inColor;
|
float4 c = tex2D(TextureSampler, texCoord) * inColor;
|
||||||
|
|
||||||
float4 stencilColor = tex2D(StencilSampler, texCoord);
|
float4 stencilColor = tex2D(StencilSampler, texCoord);
|
||||||
|
|
||||||
float aDiff = stencilColor.a - aCutoff;
|
float aDiff = stencilColor.a - aCutoff;
|
||||||
@@ -30,6 +31,18 @@ float4 main(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord
|
|||||||
min(aDiff * aMultiplier, c.a));
|
min(aDiff * aMultiplier, c.a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float4 solidColorStencil(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||||
|
{
|
||||||
|
float4 c = tex2D(TextureSampler, texCoord) * inColor;
|
||||||
|
float4 stencilColor = tex2D(StencilSampler, texCoord);
|
||||||
|
|
||||||
|
float aDiff = stencilColor.a - aCutoff;
|
||||||
|
|
||||||
|
clip(aDiff);
|
||||||
|
|
||||||
|
return float4(solidColor.rgb, solidColor.a * min(aDiff * aMultiplier, c.a));
|
||||||
|
}
|
||||||
|
|
||||||
technique StencilShader
|
technique StencilShader
|
||||||
{
|
{
|
||||||
pass Pass1
|
pass Pass1
|
||||||
@@ -37,3 +50,11 @@ technique StencilShader
|
|||||||
PixelShader = compile ps_2_0 main();
|
PixelShader = compile ps_2_0 main();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
technique StencilShaderSolidColor
|
||||||
|
{
|
||||||
|
pass Pass1
|
||||||
|
{
|
||||||
|
PixelShader = compile ps_2_0 solidColorStencil();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,10 +30,17 @@ float xLosAlpha;
|
|||||||
|
|
||||||
float4 xColor;
|
float4 xColor;
|
||||||
|
|
||||||
|
float blurDistance;
|
||||||
|
|
||||||
float4 mainPS(VertexShaderOutput input) : COLOR0
|
float4 mainPS(VertexShaderOutput input) : COLOR0
|
||||||
{
|
{
|
||||||
float4 sampleColor = xTexture.Sample(TextureSampler, input.TexCoords);
|
float4 sampleColor = xTexture.Sample(TextureSampler, input.TexCoords);
|
||||||
float4 losColor = xLosTexture.Sample(LosSampler, input.TexCoords);
|
|
||||||
|
float4 losColor = xLosTexture.Sample(LosSampler, float2(input.TexCoords.x + blurDistance, input.TexCoords.y + blurDistance));
|
||||||
|
losColor += xLosTexture.Sample(LosSampler, float2(input.TexCoords.x - blurDistance, input.TexCoords.y - blurDistance));
|
||||||
|
losColor += xLosTexture.Sample(LosSampler, float2(input.TexCoords.x + blurDistance, input.TexCoords.y - blurDistance));
|
||||||
|
losColor += xLosTexture.Sample(LosSampler, float2(input.TexCoords.x - blurDistance, input.TexCoords.y + blurDistance));
|
||||||
|
losColor = losColor * 0.25f;
|
||||||
|
|
||||||
float obscureAmount = 1.0f - losColor.r;
|
float obscureAmount = 1.0f - losColor.r;
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,16 @@ float xLosAlpha;
|
|||||||
|
|
||||||
float4 xColor;
|
float4 xColor;
|
||||||
|
|
||||||
|
float blurDistance;
|
||||||
|
|
||||||
float4 mainPS(VertexShaderOutput input) : COLOR0
|
float4 mainPS(VertexShaderOutput input) : COLOR0
|
||||||
{
|
{
|
||||||
float4 sampleColor = tex2D(TextureSampler, input.TexCoords);
|
float4 sampleColor = tex2D(TextureSampler, input.TexCoords);
|
||||||
float4 losColor = tex2D(LosSampler, input.TexCoords);
|
float4 losColor = tex2D(LosSampler, float2(input.TexCoords.x + blurDistance, input.TexCoords.y + blurDistance));
|
||||||
|
losColor += tex2D(LosSampler, float2(input.TexCoords.x - blurDistance, input.TexCoords.y - blurDistance));
|
||||||
|
losColor += tex2D(LosSampler, float2(input.TexCoords.x + blurDistance, input.TexCoords.y - blurDistance));
|
||||||
|
losColor += tex2D(LosSampler, float2(input.TexCoords.x - blurDistance, input.TexCoords.y + blurDistance));
|
||||||
|
losColor = losColor * 0.25f;
|
||||||
|
|
||||||
float obscureAmount = 1.0f - losColor.r;
|
float obscureAmount = 1.0f - losColor.r;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>1.0.13.2</Version>
|
<Version>1.0.20.1</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>1.0.13.2</Version>
|
<Version>1.0.20.1</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
|
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>1.0.13.2</Version>
|
<Version>1.0.20.1</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
|
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
distance = Math.Min(distance, Vector2.Distance(recipient.Character.ViewTarget.WorldPosition, WorldPosition));
|
distance = Math.Min(distance, Vector2.Distance(recipient.Character.ViewTarget.WorldPosition, WorldPosition));
|
||||||
}
|
}
|
||||||
|
if (ViewTarget != null && ViewTarget != this)
|
||||||
|
{
|
||||||
|
distance = Math.Min(distance, Vector2.Distance(comparePosition, ViewTarget.WorldPosition));
|
||||||
|
}
|
||||||
|
|
||||||
float priority = 1.0f - MathUtils.InverseLerp(
|
float priority = 1.0f - MathUtils.InverseLerp(
|
||||||
NetConfig.HighPrioCharacterPositionUpdateDistance,
|
NetConfig.HighPrioCharacterPositionUpdateDistance,
|
||||||
@@ -155,8 +159,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
memInput.RemoveAt(memInput.Count - 1);
|
memInput.RemoveAt(memInput.Count - 1);
|
||||||
|
|
||||||
TransformCursorPos();
|
|
||||||
|
|
||||||
if ((dequeuedInput == InputNetFlags.None || dequeuedInput == InputNetFlags.FacingLeft) && Math.Abs(AnimController.Collider.LinearVelocity.X) < 0.005f && Math.Abs(AnimController.Collider.LinearVelocity.Y) < 0.2f)
|
if ((dequeuedInput == InputNetFlags.None || dequeuedInput == InputNetFlags.FacingLeft) && Math.Abs(AnimController.Collider.LinearVelocity.X) < 0.005f && Math.Abs(AnimController.Collider.LinearVelocity.Y) < 0.2f)
|
||||||
{
|
{
|
||||||
while (memInput.Count > 5 && memInput[memInput.Count - 1].states == dequeuedInput)
|
while (memInput.Count > 5 && memInput[memInput.Count - 1].states == dequeuedInput)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Barotrauma.Networking;
|
using Barotrauma.Networking;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -16,11 +17,10 @@ namespace Barotrauma
|
|||||||
foreach (var kvp in spawnedResources)
|
foreach (var kvp in spawnedResources)
|
||||||
{
|
{
|
||||||
msg.WriteByte((byte)kvp.Value.Count);
|
msg.WriteByte((byte)kvp.Value.Count);
|
||||||
var rotation = resourceClusters[kvp.Key].Rotation;
|
msg.WriteSingle(kvp.Value.FirstOrDefault()?.Rotation ?? 0.0f);
|
||||||
msg.WriteSingle(rotation);
|
foreach (var item in kvp.Value)
|
||||||
foreach (var r in kvp.Value)
|
|
||||||
{
|
{
|
||||||
r.WriteSpawnData(msg, r.ID, Entity.NullEntityID, 0, -1);
|
item.WriteSpawnData(msg, item.ID, Entity.NullEntityID, 0, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,9 +28,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
msg.WriteIdentifier(kvp.Key);
|
msg.WriteIdentifier(kvp.Key);
|
||||||
msg.WriteByte((byte)kvp.Value.Length);
|
msg.WriteByte((byte)kvp.Value.Length);
|
||||||
foreach (var i in kvp.Value)
|
foreach (var item in kvp.Value)
|
||||||
{
|
{
|
||||||
msg.WriteUInt16(i.ID);
|
msg.WriteUInt16(item.ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-1
@@ -6,7 +6,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Barotrauma.Steam;
|
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -1310,6 +1309,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public override bool TryPurchase(Client client, int price)
|
public override bool TryPurchase(Client client, int price)
|
||||||
{
|
{
|
||||||
|
//disconnected clients can never purchase anything
|
||||||
|
//(can happen e.g. if someone starts a vote to buy something and then disconnects)
|
||||||
|
if (client != null && !GameMain.Server.ConnectedClients.Contains(client)) { return false; }
|
||||||
|
|
||||||
Wallet wallet = GetWallet(client);
|
Wallet wallet = GetWallet(client);
|
||||||
if (!AllowedToManageWallets(client))
|
if (!AllowedToManageWallets(client))
|
||||||
{
|
{
|
||||||
@@ -1359,6 +1362,12 @@ namespace Barotrauma
|
|||||||
modeElement.Add(Settings.Save());
|
modeElement.Add(Settings.Save());
|
||||||
modeElement.Add(SaveStats());
|
modeElement.Add(SaveStats());
|
||||||
modeElement.Add(Bank.Save());
|
modeElement.Add(Bank.Save());
|
||||||
|
|
||||||
|
if (GameMain.GameSession?.EventManager != null)
|
||||||
|
{
|
||||||
|
modeElement.Add(GameMain.GameSession?.EventManager.Save());
|
||||||
|
}
|
||||||
|
|
||||||
CampaignMetadata?.Save(modeElement);
|
CampaignMetadata?.Save(modeElement);
|
||||||
Map.Save(modeElement);
|
Map.Save(modeElement);
|
||||||
CargoManager?.SavePurchasedItems(modeElement);
|
CargoManager?.SavePurchasedItems(modeElement);
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
if (item.CanClientAccess(c))
|
if (item.CanClientAccess(c))
|
||||||
{
|
{
|
||||||
|
lastReceivedTargetForce = null;
|
||||||
if (Math.Abs(newTargetForce - targetForce) > 0.01f)
|
if (Math.Abs(newTargetForce - targetForce) > 0.01f)
|
||||||
{
|
{
|
||||||
GameServer.Log(GameServer.CharacterLogName(c.Character) + " set the force of " + item.Name + " to " + (int)(newTargetForce) + " %", ServerLog.MessageType.ItemInteraction);
|
GameServer.Log(GameServer.CharacterLogName(c.Character) + " set the force of " + item.Name + " to " + (int)(newTargetForce) + " %", ServerLog.MessageType.ItemInteraction);
|
||||||
}
|
}
|
||||||
|
|
||||||
targetForce = newTargetForce;
|
targetForce = newTargetForce;
|
||||||
User = c.Character;
|
User = c.Character;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ namespace Barotrauma.Items.Components
|
|||||||
//already connected, no need to do anything
|
//already connected, no need to do anything
|
||||||
if (Connections[i].Wires.Contains(newWire)) { continue; }
|
if (Connections[i].Wires.Contains(newWire)) { continue; }
|
||||||
|
|
||||||
newWire.Connect(Connections[i], true, true);
|
newWire.TryConnect(Connections[i], true, true);
|
||||||
Connections[i].TryAddLink(newWire);
|
Connections[i].TryAddLink(newWire);
|
||||||
|
|
||||||
var otherConnection = newWire.OtherConnection(Connections[i]);
|
var otherConnection = newWire.OtherConnection(Connections[i]);
|
||||||
|
|||||||
@@ -23,15 +23,13 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
public override Voting Voting { get; }
|
public override Voting Voting { get; }
|
||||||
|
|
||||||
private string serverName;
|
|
||||||
public string ServerName
|
public string ServerName
|
||||||
{
|
{
|
||||||
get { return serverName; }
|
get { return ServerSettings.ServerName; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(value)) { return; }
|
if (string.IsNullOrEmpty(value)) { return; }
|
||||||
|
ServerSettings.ServerName = value;
|
||||||
serverName = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,8 +134,6 @@ namespace Barotrauma.Networking
|
|||||||
name = name.Substring(0, NetConfig.ServerNameMaxLength);
|
name = name.Substring(0, NetConfig.ServerNameMaxLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.serverName = name;
|
|
||||||
|
|
||||||
LastClientListUpdateID = 0;
|
LastClientListUpdateID = 0;
|
||||||
|
|
||||||
ServerSettings = new ServerSettings(this, name, port, queryPort, maxPlayers, isPublic, attemptUPnP, listenIp);
|
ServerSettings = new ServerSettings(this, name, port, queryPort, maxPlayers, isPublic, attemptUPnP, listenIp);
|
||||||
@@ -1680,38 +1676,54 @@ namespace Barotrauma.Networking
|
|||||||
//characters or items spawned mid-round don't necessarily exist at the client's end yet
|
//characters or items spawned mid-round don't necessarily exist at the client's end yet
|
||||||
if (!c.NeedsMidRoundSync)
|
if (!c.NeedsMidRoundSync)
|
||||||
{
|
{
|
||||||
foreach (Character character in Character.CharacterList)
|
Character clientCharacter = c.Character;
|
||||||
|
foreach (Character otherCharacter in Character.CharacterList)
|
||||||
{
|
{
|
||||||
if (!character.Enabled) { continue; }
|
if (!otherCharacter.Enabled) { continue; }
|
||||||
if (c.SpectatePos == null)
|
if (c.SpectatePos == null)
|
||||||
{
|
{
|
||||||
float distSqr = Vector2.DistanceSquared(character.WorldPosition, c.Character.WorldPosition);
|
//not spectating ->
|
||||||
if (c.Character.ViewTarget != null)
|
// check if the client's character, or the entity they're viewing,
|
||||||
|
// is close enough to the other character or the entity the other character is viewing
|
||||||
|
float distSqr = GetShortestDistance(clientCharacter.WorldPosition, otherCharacter);
|
||||||
|
if (clientCharacter.ViewTarget != null && clientCharacter.ViewTarget != clientCharacter)
|
||||||
{
|
{
|
||||||
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(character.WorldPosition, c.Character.ViewTarget.WorldPosition));
|
distSqr = Math.Min(distSqr, GetShortestDistance(clientCharacter.ViewTarget.WorldPosition, otherCharacter));
|
||||||
}
|
}
|
||||||
if (distSqr >= MathUtils.Pow2(character.Params.DisableDistance)) { continue; }
|
if (distSqr >= MathUtils.Pow2(otherCharacter.Params.DisableDistance)) { continue; }
|
||||||
}
|
}
|
||||||
else
|
else if (otherCharacter != clientCharacter)
|
||||||
{
|
{
|
||||||
if (character != c.Character && Vector2.DistanceSquared(character.WorldPosition, c.SpectatePos.Value) >= MathUtils.Pow2(character.Params.DisableDistance))
|
//spectating ->
|
||||||
{
|
// check if the position the client is viewing
|
||||||
continue;
|
// is close enough to the other character or the entity the other character is viewing
|
||||||
}
|
if (GetShortestDistance(c.SpectatePos.Value, otherCharacter) >= MathUtils.Pow2(otherCharacter.Params.DisableDistance)) { continue; }
|
||||||
}
|
}
|
||||||
|
|
||||||
float updateInterval = character.GetPositionUpdateInterval(c);
|
static float GetShortestDistance(Vector2 viewPos, Character targetCharacter)
|
||||||
c.PositionUpdateLastSent.TryGetValue(character, out float lastSent);
|
{
|
||||||
|
float distSqr = Vector2.DistanceSquared(viewPos, targetCharacter.WorldPosition);
|
||||||
|
if (targetCharacter.ViewTarget != null && targetCharacter.ViewTarget != targetCharacter)
|
||||||
|
{
|
||||||
|
//if the character is viewing something (far-away turret?),
|
||||||
|
//we might want to send updates about it to the spectating client even though they're far away from the actual character
|
||||||
|
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(viewPos, targetCharacter.ViewTarget.WorldPosition));
|
||||||
|
}
|
||||||
|
return distSqr;
|
||||||
|
}
|
||||||
|
|
||||||
|
float updateInterval = otherCharacter.GetPositionUpdateInterval(c);
|
||||||
|
c.PositionUpdateLastSent.TryGetValue(otherCharacter, out float lastSent);
|
||||||
if (lastSent > NetTime.Now)
|
if (lastSent > NetTime.Now)
|
||||||
{
|
{
|
||||||
//sent in the future -> can't be right, remove
|
//sent in the future -> can't be right, remove
|
||||||
c.PositionUpdateLastSent.Remove(character);
|
c.PositionUpdateLastSent.Remove(otherCharacter);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (lastSent > NetTime.Now - updateInterval) { continue; }
|
if (lastSent > NetTime.Now - updateInterval) { continue; }
|
||||||
}
|
}
|
||||||
if (!c.PendingPositionUpdates.Contains(character)) { c.PendingPositionUpdates.Enqueue(character); }
|
if (!c.PendingPositionUpdates.Contains(otherCharacter)) { c.PendingPositionUpdates.Enqueue(otherCharacter); }
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Submarine sub in Submarine.Loaded)
|
foreach (Submarine sub in Submarine.Loaded)
|
||||||
@@ -3138,7 +3150,7 @@ namespace Barotrauma.Networking
|
|||||||
default:
|
default:
|
||||||
if (command != "")
|
if (command != "")
|
||||||
{
|
{
|
||||||
if (command.ToLower() == serverName.ToLower())
|
if (command.ToLower() == ServerName.ToLower())
|
||||||
{
|
{
|
||||||
//a private message to the host
|
//a private message to the host
|
||||||
if (OwnerConnection != null)
|
if (OwnerConnection != null)
|
||||||
@@ -3193,7 +3205,7 @@ namespace Barotrauma.Networking
|
|||||||
//msg sent by the server
|
//msg sent by the server
|
||||||
if (senderCharacter == null)
|
if (senderCharacter == null)
|
||||||
{
|
{
|
||||||
senderName = serverName;
|
senderName = ServerName;
|
||||||
}
|
}
|
||||||
else //msg sent by an AI character
|
else //msg sent by an AI character
|
||||||
{
|
{
|
||||||
@@ -3227,7 +3239,7 @@ namespace Barotrauma.Networking
|
|||||||
//msg sent by the server
|
//msg sent by the server
|
||||||
if (senderCharacter == null)
|
if (senderCharacter == null)
|
||||||
{
|
{
|
||||||
senderName = serverName;
|
senderName = ServerName;
|
||||||
}
|
}
|
||||||
else //sent by an AI character, not allowed when the game is not running
|
else //sent by an AI character, not allowed when the game is not running
|
||||||
{
|
{
|
||||||
@@ -3459,33 +3471,35 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SwitchSubmarine()
|
public bool TrySwitchSubmarine()
|
||||||
{
|
{
|
||||||
if (Voting.ActiveVote is not Voting.SubmarineVote subVote) { return; }
|
if (Voting.ActiveVote is not Voting.SubmarineVote subVote) { return false; }
|
||||||
|
|
||||||
SubmarineInfo targetSubmarine = subVote.Sub;
|
SubmarineInfo targetSubmarine = subVote.Sub;
|
||||||
VoteType voteType = Voting.ActiveVote.VoteType;
|
VoteType voteType = Voting.ActiveVote.VoteType;
|
||||||
Client starter = Voting.ActiveVote.VoteStarter;
|
Client starter = Voting.ActiveVote.VoteStarter;
|
||||||
|
|
||||||
|
bool purchaseFailed = false;
|
||||||
switch (voteType)
|
switch (voteType)
|
||||||
{
|
{
|
||||||
case VoteType.PurchaseAndSwitchSub:
|
case VoteType.PurchaseAndSwitchSub:
|
||||||
case VoteType.PurchaseSub:
|
case VoteType.PurchaseSub:
|
||||||
// Pay for submarine
|
// Pay for submarine
|
||||||
GameMain.GameSession.PurchaseSubmarine(targetSubmarine, starter);
|
purchaseFailed = !GameMain.GameSession.TryPurchaseSubmarine(targetSubmarine, starter);
|
||||||
break;
|
break;
|
||||||
case VoteType.SwitchSub:
|
case VoteType.SwitchSub:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (voteType != VoteType.PurchaseSub)
|
if (voteType != VoteType.PurchaseSub && !purchaseFailed)
|
||||||
{
|
{
|
||||||
GameMain.GameSession.SwitchSubmarine(targetSubmarine, subVote.TransferItems, starter);
|
GameMain.GameSession.SwitchSubmarine(targetSubmarine, subVote.TransferItems, starter);
|
||||||
}
|
}
|
||||||
|
|
||||||
Voting.StopSubmarineVote(true);
|
Voting.StopSubmarineVote(passed: !purchaseFailed);
|
||||||
|
return !purchaseFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateClientPermissions(Client client)
|
public void UpdateClientPermissions(Client client)
|
||||||
|
|||||||
@@ -370,6 +370,8 @@ namespace Barotrauma.Networking
|
|||||||
"192-255",
|
"192-255",
|
||||||
"384-591",
|
"384-591",
|
||||||
"1024-1279",
|
"1024-1279",
|
||||||
|
"4352-4607", //Hangul Jamo
|
||||||
|
"44032-55215", //Hangul Syllables
|
||||||
"19968-21327","21329-40959","13312-19903","131072-173791","173824-178207","178208-183983","63744-64255","194560-195103" //CJK
|
"19968-21327","21329-40959","13312-19903","131072-173791","173824-178207","178208-183983","63744-64255","194560-195103" //CJK
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,11 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (passed)
|
if (passed)
|
||||||
{
|
{
|
||||||
GameMain.Server?.SwitchSubmarine();
|
if (GameMain.Server != null && !GameMain.Server.TrySwitchSubmarine())
|
||||||
|
{
|
||||||
|
passed = false;
|
||||||
|
State = VoteState.Failed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -71,6 +71,15 @@ namespace Barotrauma
|
|||||||
StrikesResetInterval = 60,
|
StrikesResetInterval = 60,
|
||||||
StrikeThreshold = 6;
|
StrikeThreshold = 6;
|
||||||
|
|
||||||
|
private const int MinPacketLimitMultipler = 1;
|
||||||
|
|
||||||
|
private static int GetMaxPacketLimit(ServerSettings settings)
|
||||||
|
=> (int)MathF.Ceiling(
|
||||||
|
settings.MaxPacketAmount *
|
||||||
|
MathF.Max(
|
||||||
|
settings.TickRate / (float)ServerSettings.DefaultTickRate,
|
||||||
|
MinPacketLimitMultipler)); // Prevent the rate limit multiplier from being less than 1.
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the server receives a packet to start logging how much time it takes to process.
|
/// Called when the server receives a packet to start logging how much time it takes to process.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -122,11 +131,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void StartFor(Client client)
|
private void StartFor(Client client)
|
||||||
{
|
{
|
||||||
if (!clients.ContainsKey(client))
|
clients.TryAdd(client, new OffenseData());
|
||||||
{
|
|
||||||
clients.Add(client, new OffenseData());
|
|
||||||
}
|
|
||||||
|
|
||||||
clients[client].Stopwatch.Start();
|
clients[client].Stopwatch.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +154,7 @@ namespace Barotrauma
|
|||||||
if (GameMain.Server?.ServerSettings is not { } settings) { return; }
|
if (GameMain.Server?.ServerSettings is not { } settings) { return; }
|
||||||
|
|
||||||
// client is sending too many packets, kick them
|
// client is sending too many packets, kick them
|
||||||
if (data.PacketCount > settings.MaxPacketAmount && settings.MaxPacketAmount > ServerSettings.PacketLimitMin)
|
if (data.PacketCount > GetMaxPacketLimit(settings) && settings.MaxPacketAmount > ServerSettings.PacketLimitMin)
|
||||||
{
|
{
|
||||||
AttemptKickClient(client, TextManager.Get("PacketLimitKicked"));
|
AttemptKickClient(client, TextManager.Get("PacketLimitKicked"));
|
||||||
clients.Remove(client);
|
clients.Remove(client);
|
||||||
@@ -216,7 +221,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (GameMain.Server?.ServerSettings is { MaxPacketAmount: > ServerSettings.PacketLimitMin } settings)
|
if (GameMain.Server?.ServerSettings is { MaxPacketAmount: > ServerSettings.PacketLimitMin } settings)
|
||||||
{
|
{
|
||||||
if (data.PacketCount > settings.MaxPacketAmount * 0.9f)
|
if (data.PacketCount > GetMaxPacketLimit(settings) * 0.9f)
|
||||||
{
|
{
|
||||||
GameServer.Log($"{NetworkMember.ClientLogName(client)} is sending a lot of packets and almost got kicked! ({data.PacketCount}).", ServerLog.MessageType.DoSProtection);
|
GameServer.Log($"{NetworkMember.ClientLogName(client)} is sending a lot of packets and almost got kicked! ({data.PacketCount}).", ServerLog.MessageType.DoSProtection);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>1.0.13.2</Version>
|
<Version>1.0.20.1</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
|
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
|
||||||
|
|||||||
@@ -95,10 +95,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (visibleHulls == null)
|
visibleHulls ??= Character.GetVisibleHulls();
|
||||||
{
|
|
||||||
visibleHulls = Character.GetVisibleHulls();
|
|
||||||
}
|
|
||||||
return visibleHulls;
|
return visibleHulls;
|
||||||
}
|
}
|
||||||
private set
|
private set
|
||||||
@@ -425,14 +422,9 @@ namespace Barotrauma
|
|||||||
var door = gap.ConnectedDoor;
|
var door = gap.ConnectedDoor;
|
||||||
if (door != null)
|
if (door != null)
|
||||||
{
|
{
|
||||||
if (!door.CanBeTraversed)
|
if (!pathSteering.CanAccessDoor(door))
|
||||||
{
|
{
|
||||||
if (!door.HasAccess(Character))
|
continue;
|
||||||
{
|
|
||||||
if (!canAttackDoors) { continue; }
|
|
||||||
// Treat doors that don't have access to like they were farther, because it will take time to break them.
|
|
||||||
multiplier = 5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -473,7 +465,7 @@ namespace Barotrauma
|
|||||||
Vector2 diff = EscapeTarget.WorldPosition - Character.WorldPosition;
|
Vector2 diff = EscapeTarget.WorldPosition - Character.WorldPosition;
|
||||||
float sqrDist = diff.LengthSquared();
|
float sqrDist = diff.LengthSquared();
|
||||||
bool isClose = sqrDist < MathUtils.Pow2(100);
|
bool isClose = sqrDist < MathUtils.Pow2(100);
|
||||||
if (Character.CurrentHull == null || isClose && !isClosedDoor || pathSteering == null || IsCurrentPathUnreachable || IsCurrentPathFinished)
|
if (Character.CurrentHull == null || (isClose && !isClosedDoor) || pathSteering == null || IsCurrentPathUnreachable || IsCurrentPathFinished)
|
||||||
{
|
{
|
||||||
// Very close to the target, outside, or at the end of the path -> try to steer through the gap
|
// Very close to the target, outside, or at the end of the path -> try to steer through the gap
|
||||||
Character.ReleaseSecondaryItem();
|
Character.ReleaseSecondaryItem();
|
||||||
|
|||||||
@@ -369,7 +369,13 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else if (targetCharacter.AIController is EnemyAIController enemy)
|
else if (targetCharacter.AIController is EnemyAIController enemy)
|
||||||
{
|
{
|
||||||
if (targetCharacter.IsHusk && AIParams.HasTag("husk"))
|
if (enemy.PetBehavior != null && (PetBehavior != null || AIParams.HasTag("pet")))
|
||||||
|
{
|
||||||
|
// Pets see other pets as pets by default.
|
||||||
|
// Monsters see them only as pet only when they have a matching ai target. Otherwise they use the other tags, specified below.
|
||||||
|
targetingTag = "pet";
|
||||||
|
}
|
||||||
|
else if (targetCharacter.IsHusk && AIParams.HasTag("husk"))
|
||||||
{
|
{
|
||||||
targetingTag = "husk";
|
targetingTag = "husk";
|
||||||
}
|
}
|
||||||
@@ -695,6 +701,9 @@ namespace Barotrauma
|
|||||||
// Can't target characters of same species/group because that would make us hostile to all friendly characters in the same species/group.
|
// Can't target characters of same species/group because that would make us hostile to all friendly characters in the same species/group.
|
||||||
if (Character.IsSameSpeciesOrGroup(c)) { return false; }
|
if (Character.IsSameSpeciesOrGroup(c)) { return false; }
|
||||||
if (targetCharacter.IsSameSpeciesOrGroup(c)) { return false; }
|
if (targetCharacter.IsSameSpeciesOrGroup(c)) { return false; }
|
||||||
|
//don't try to attack targets in a sub that belongs to a different team
|
||||||
|
//(for example, targets in an outpost if we're in the main sub)
|
||||||
|
if (c.Submarine?.TeamID != Character.Submarine?.TeamID) { return false; }
|
||||||
if (c.IsPlayer || Character.IsOnFriendlyTeam(c))
|
if (c.IsPlayer || Character.IsOnFriendlyTeam(c))
|
||||||
{
|
{
|
||||||
return a.Damage >= selectedTargetingParams.Threshold;
|
return a.Damage >= selectedTargetingParams.Threshold;
|
||||||
@@ -894,7 +903,7 @@ namespace Barotrauma
|
|||||||
_previousAttackLimb?.attack is Attack previousAttack && (previousAttack.AfterAttack != AIBehaviorAfterAttack.FallBack || previousAttack.CoolDownTimer <= 0)))
|
_previousAttackLimb?.attack is Attack previousAttack && (previousAttack.AfterAttack != AIBehaviorAfterAttack.FallBack || previousAttack.CoolDownTimer <= 0)))
|
||||||
{
|
{
|
||||||
// Keep heading to the last known position of the target
|
// Keep heading to the last known position of the target
|
||||||
var memory = GetTargetMemory(target, false);
|
var memory = GetTargetMemory(target);
|
||||||
if (memory != null)
|
if (memory != null)
|
||||||
{
|
{
|
||||||
var location = memory.Location;
|
var location = memory.Location;
|
||||||
@@ -981,7 +990,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PathSteering.SetPath(path);
|
PathSteering.SetPath(patrolTarget.SimPosition, path);
|
||||||
patrolTimerMargin = 0;
|
patrolTimerMargin = 0;
|
||||||
newPatrolTargetTimer = newPatrolTargetIntervalMax * Rand.Range(0.5f, 1.5f);
|
newPatrolTargetTimer = newPatrolTargetIntervalMax * Rand.Range(0.5f, 1.5f);
|
||||||
searchingNewHull = false;
|
searchingNewHull = false;
|
||||||
@@ -1088,13 +1097,13 @@ namespace Barotrauma
|
|||||||
Character owner = GetOwner(item);
|
Character owner = GetOwner(item);
|
||||||
if (owner != null)
|
if (owner != null)
|
||||||
{
|
{
|
||||||
if (Character.IsFriendly(owner))
|
if (Character.IsFriendly(owner) || owner.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI))
|
||||||
{
|
{
|
||||||
ResetAITarget();
|
ResetAITarget();
|
||||||
State = AIState.Idle;
|
State = AIState.Idle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (!owner.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI))
|
else
|
||||||
{
|
{
|
||||||
SelectedAiTarget = owner.AiTarget;
|
SelectedAiTarget = owner.AiTarget;
|
||||||
}
|
}
|
||||||
@@ -2186,7 +2195,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AITargetMemory targetMemory = GetTargetMemory(attacker.AiTarget, addIfNotFound: true);
|
AITargetMemory targetMemory = GetTargetMemory(attacker.AiTarget, addIfNotFound: true, keepAlive: true);
|
||||||
targetMemory.Priority += GetRelativeDamage(attackResult.Damage, Character.Vitality) * AIParams.AggressionHurt;
|
targetMemory.Priority += GetRelativeDamage(attackResult.Damage, Character.Vitality) * AIParams.AggressionHurt;
|
||||||
|
|
||||||
// Only allow to react once. Otherwise would attack the target with only a fraction of a cooldown
|
// Only allow to react once. Otherwise would attack the target with only a fraction of a cooldown
|
||||||
@@ -2531,8 +2540,10 @@ namespace Barotrauma
|
|||||||
if (Math.Abs(limbDiff.X) < itemBodyExtent &&
|
if (Math.Abs(limbDiff.X) < itemBodyExtent &&
|
||||||
Math.Abs(limbDiff.Y) < Character.AnimController.Collider.GetMaxExtent() + Character.AnimController.ColliderHeightFromFloor)
|
Math.Abs(limbDiff.Y) < Character.AnimController.Collider.GetMaxExtent() + Character.AnimController.ColliderHeightFromFloor)
|
||||||
{
|
{
|
||||||
|
Vector2 velocity = limbDiff;
|
||||||
|
if (limbDiff.LengthSquared() > 0.01f) { velocity = Vector2.Normalize(velocity); }
|
||||||
item.body.LinearVelocity *= 0.9f;
|
item.body.LinearVelocity *= 0.9f;
|
||||||
item.body.LinearVelocity -= limbDiff * 0.25f;
|
item.body.LinearVelocity -= velocity * 0.25f;
|
||||||
bool wasBroken = item.Condition <= 0.0f;
|
bool wasBroken = item.Condition <= 0.0f;
|
||||||
item.AddDamage(Character, item.WorldPosition, new Attack(0.0f, 0.0f, 0.0f, 0.0f, 0.02f * Character.Params.EatingSpeed), deltaTime);
|
item.AddDamage(Character, item.WorldPosition, new Attack(0.0f, 0.0f, 0.0f, 0.0f, 0.02f * Character.Params.EatingSpeed), deltaTime);
|
||||||
Character.ApplyStatusEffects(ActionType.OnEating, deltaTime);
|
Character.ApplyStatusEffects(ActionType.OnEating, deltaTime);
|
||||||
@@ -2924,7 +2935,8 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (targetParams.State == AIState.Eat && Character.Params.Health.HealthRegenerationWhenEating > 0)
|
//no need to eat if the character is already in full health (except if it's a pet - pets actually need to eat to stay alive, not just to regain health)
|
||||||
|
if (targetParams.State == AIState.Eat && Character.Params.Health.HealthRegenerationWhenEating > 0 && !Character.IsPet)
|
||||||
{
|
{
|
||||||
valueModifier *= MathHelper.Lerp(1f, 0.1f, Character.HealthPercentage / 100f);
|
valueModifier *= MathHelper.Lerp(1f, 0.1f, Character.HealthPercentage / 100f);
|
||||||
}
|
}
|
||||||
@@ -3021,7 +3033,7 @@ namespace Barotrauma
|
|||||||
//if the target is very close, the distance doesn't make much difference
|
//if the target is very close, the distance doesn't make much difference
|
||||||
// -> just ignore the distance and target whatever has the highest priority
|
// -> just ignore the distance and target whatever has the highest priority
|
||||||
dist = Math.Max(dist, 100.0f);
|
dist = Math.Max(dist, 100.0f);
|
||||||
AITargetMemory targetMemory = GetTargetMemory(aiTarget, addIfNotFound: true);
|
AITargetMemory targetMemory = GetTargetMemory(aiTarget, addIfNotFound: true, keepAlive: SelectedAiTarget != aiTarget);
|
||||||
if (Character.Submarine != null && !Character.Submarine.Info.IsRuin && Character.CurrentHull != null)
|
if (Character.Submarine != null && !Character.Submarine.Info.IsRuin && Character.CurrentHull != null)
|
||||||
{
|
{
|
||||||
float diff = Math.Abs(toTarget.Y) - Character.CurrentHull.Size.Y;
|
float diff = Math.Abs(toTarget.Y) - Character.CurrentHull.Size.Y;
|
||||||
@@ -3090,12 +3102,20 @@ namespace Barotrauma
|
|||||||
if (aiTarget.Entity is Item i)
|
if (aiTarget.Entity is Item i)
|
||||||
{
|
{
|
||||||
Character owner = GetOwner(i);
|
Character owner = GetOwner(i);
|
||||||
// Don't target items that we own.
|
|
||||||
// This is a rare case, and almost entirely related to Humanhusks, so let's check it last to reduce unnecessary checks (although the check shouldn't be expensive)
|
|
||||||
if (owner == Character) { continue; }
|
if (owner == Character) { continue; }
|
||||||
if (owner != null && (Character.IsFriendly(owner) || owner.AiTarget != null && ignoredTargets.Contains(owner.AiTarget)))
|
if (owner != null)
|
||||||
{
|
{
|
||||||
continue;
|
if (owner.AiTarget != null && ignoredTargets.Contains(owner.AiTarget)) { continue; }
|
||||||
|
if (Character.IsFriendly(owner))
|
||||||
|
{
|
||||||
|
// Don't target items that we own. This is a rare case, and almost entirely related to Humanhusks (in the vanilla game).
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (owner.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI))
|
||||||
|
{
|
||||||
|
// ignore if owner is tagged to be explicitly ignored (Feign Death)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (targetCharacter != null)
|
if (targetCharacter != null)
|
||||||
@@ -3418,7 +3438,7 @@ namespace Barotrauma
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AITargetMemory GetTargetMemory(AITarget target, bool addIfNotFound)
|
private AITargetMemory GetTargetMemory(AITarget target, bool addIfNotFound = false, bool keepAlive = false)
|
||||||
{
|
{
|
||||||
if (!targetMemories.TryGetValue(target, out AITargetMemory memory))
|
if (!targetMemories.TryGetValue(target, out AITargetMemory memory))
|
||||||
{
|
{
|
||||||
@@ -3428,9 +3448,8 @@ namespace Barotrauma
|
|||||||
targetMemories.Add(target, memory);
|
targetMemories.Add(target, memory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (addIfNotFound)
|
if (keepAlive)
|
||||||
{
|
{
|
||||||
// Keep the memory alive.
|
|
||||||
memory.Priority = Math.Max(memory.Priority, minPriority);
|
memory.Priority = Math.Max(memory.Priority, minPriority);
|
||||||
}
|
}
|
||||||
return memory;
|
return memory;
|
||||||
@@ -3446,7 +3465,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else if (CanPerceive(_selectedAiTarget, checkVisibility: false))
|
else if (CanPerceive(_selectedAiTarget, checkVisibility: false))
|
||||||
{
|
{
|
||||||
var memory = GetTargetMemory(_selectedAiTarget, false);
|
var memory = GetTargetMemory(_selectedAiTarget);
|
||||||
if (memory != null)
|
if (memory != null)
|
||||||
{
|
{
|
||||||
memory.Location = _selectedAiTarget.WorldPosition;
|
memory.Location = _selectedAiTarget.WorldPosition;
|
||||||
@@ -3504,10 +3523,10 @@ namespace Barotrauma
|
|||||||
private readonly float stateResetCooldown = 10;
|
private readonly float stateResetCooldown = 10;
|
||||||
private float stateResetTimer;
|
private float stateResetTimer;
|
||||||
private bool isStateChanged;
|
private bool isStateChanged;
|
||||||
private readonly Dictionary<AITrigger, CharacterParams.TargetParams> activeTriggers = new Dictionary<AITrigger, CharacterParams.TargetParams>();
|
private readonly Dictionary<StatusEffect.AITrigger, CharacterParams.TargetParams> activeTriggers = new Dictionary<StatusEffect.AITrigger, CharacterParams.TargetParams>();
|
||||||
private readonly HashSet<AITrigger> inactiveTriggers = new HashSet<AITrigger>();
|
private readonly HashSet<StatusEffect.AITrigger> inactiveTriggers = new HashSet<StatusEffect.AITrigger>();
|
||||||
|
|
||||||
public void LaunchTrigger(AITrigger trigger)
|
public void LaunchTrigger(StatusEffect.AITrigger trigger)
|
||||||
{
|
{
|
||||||
if (trigger.IsTriggered) { return; }
|
if (trigger.IsTriggered) { return; }
|
||||||
if (activeTriggers.ContainsKey(trigger)) { return; }
|
if (activeTriggers.ContainsKey(trigger)) { return; }
|
||||||
@@ -3527,7 +3546,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (var triggerObject in activeTriggers)
|
foreach (var triggerObject in activeTriggers)
|
||||||
{
|
{
|
||||||
AITrigger trigger = triggerObject.Key;
|
StatusEffect.AITrigger trigger = triggerObject.Key;
|
||||||
if (trigger.IsPermanent) { continue; }
|
if (trigger.IsPermanent) { continue; }
|
||||||
trigger.UpdateTimer(deltaTime);
|
trigger.UpdateTimer(deltaTime);
|
||||||
if (!trigger.IsActive)
|
if (!trigger.IsActive)
|
||||||
@@ -3537,7 +3556,7 @@ namespace Barotrauma
|
|||||||
inactiveTriggers.Add(trigger);
|
inactiveTriggers.Add(trigger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (AITrigger trigger in inactiveTriggers)
|
foreach (StatusEffect.AITrigger trigger in inactiveTriggers)
|
||||||
{
|
{
|
||||||
activeTriggers.Remove(trigger);
|
activeTriggers.Remove(trigger);
|
||||||
}
|
}
|
||||||
@@ -3643,7 +3662,11 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
isStateChanged = true;
|
isStateChanged = true;
|
||||||
SetStateResetTimer();
|
SetStateResetTimer();
|
||||||
ChangeParams(target.SpeciesName, state, priority, ignoreAttacksIfNotInSameSub: !target.IsHuman);
|
if (!Character.IsPet || !target.IsHuman)
|
||||||
|
{
|
||||||
|
//don't turn pets hostile to all humans when attacked by one
|
||||||
|
ChangeParams(target.SpeciesName, state, priority, ignoreAttacksIfNotInSameSub: !target.IsHuman);
|
||||||
|
}
|
||||||
if (target.IsHuman)
|
if (target.IsHuman)
|
||||||
{
|
{
|
||||||
priority = GetTargetParams("human")?.Priority;
|
priority = GetTargetParams("human")?.Priority;
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ namespace Barotrauma
|
|||||||
public readonly HashSet<Hull> UnsafeHulls = new HashSet<Hull>();
|
public readonly HashSet<Hull> UnsafeHulls = new HashSet<Hull>();
|
||||||
public readonly List<Item> IgnoredItems = new List<Item>();
|
public readonly List<Item> IgnoredItems = new List<Item>();
|
||||||
|
|
||||||
|
private readonly HashSet<Hull> dirtyHullSafetyCalculations = new HashSet<Hull>();
|
||||||
|
|
||||||
private float respondToAttackTimer;
|
private float respondToAttackTimer;
|
||||||
private const float RespondToAttackInterval = 1.0f;
|
private const float RespondToAttackInterval = 1.0f;
|
||||||
private bool wasConscious;
|
private bool wasConscious;
|
||||||
@@ -436,6 +438,7 @@ namespace Barotrauma
|
|||||||
foreach (Hull h in VisibleHulls)
|
foreach (Hull h in VisibleHulls)
|
||||||
{
|
{
|
||||||
PropagateHullSafety(Character, h);
|
PropagateHullSafety(Character, h);
|
||||||
|
dirtyHullSafetyCalculations.Remove(h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -443,9 +446,15 @@ namespace Barotrauma
|
|||||||
foreach (Hull h in VisibleHulls)
|
foreach (Hull h in VisibleHulls)
|
||||||
{
|
{
|
||||||
RefreshHullSafety(h);
|
RefreshHullSafety(h);
|
||||||
|
dirtyHullSafetyCalculations.Remove(h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
foreach (Hull h in dirtyHullSafetyCalculations)
|
||||||
|
{
|
||||||
|
RefreshHullSafety(h);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
dirtyHullSafetyCalculations.Clear();
|
||||||
if (reportProblemsTimer <= 0.0f)
|
if (reportProblemsTimer <= 0.0f)
|
||||||
{
|
{
|
||||||
if (Character.Submarine != null && (Character.Submarine.TeamID == Character.TeamID || Character.Submarine.TeamID == Character.OriginalTeamID || Character.IsEscorted) && !Character.Submarine.Info.IsWreck)
|
if (Character.Submarine != null && (Character.Submarine.TeamID == Character.TeamID || Character.Submarine.TeamID == Character.OriginalTeamID || Character.IsEscorted) && !Character.Submarine.Info.IsWreck)
|
||||||
@@ -615,7 +624,7 @@ namespace Barotrauma
|
|||||||
ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn) ||
|
ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn) ||
|
||||||
Character.CurrentHull.OxygenPercentage < HULL_LOW_OXYGEN_PERCENTAGE + 10 ||
|
Character.CurrentHull.OxygenPercentage < HULL_LOW_OXYGEN_PERCENTAGE + 10 ||
|
||||||
Character.CurrentHull.IsWetRoom;
|
Character.CurrentHull.IsWetRoom;
|
||||||
bool IsOrderedToWait() => Character.IsOnPlayerTeam && ObjectiveManager.CurrentOrder is AIObjectiveGoTo goTo && goTo.Target == Character;
|
bool IsOrderedToWait() => Character.IsOnPlayerTeam && ObjectiveManager.CurrentOrder is AIObjectiveGoTo { IsWaitOrder: true };
|
||||||
bool removeDivingSuit = !shouldKeepTheGearOn && !IsOrderedToWait();
|
bool removeDivingSuit = !shouldKeepTheGearOn && !IsOrderedToWait();
|
||||||
if (shouldActOnSuffocation && Character.CurrentHull.Oxygen > 0 && (!isCurrentObjectiveFindSafety || Character.OxygenAvailable < 1))
|
if (shouldActOnSuffocation && Character.CurrentHull.Oxygen > 0 && (!isCurrentObjectiveFindSafety || Character.OxygenAvailable < 1))
|
||||||
{
|
{
|
||||||
@@ -900,7 +909,7 @@ namespace Barotrauma
|
|||||||
var container = i.GetComponent<ItemContainer>();
|
var container = i.GetComponent<ItemContainer>();
|
||||||
if (container == null) { return 0; }
|
if (container == null) { return 0; }
|
||||||
if (!container.Inventory.CanBePut(containableItem)) { return 0; }
|
if (!container.Inventory.CanBePut(containableItem)) { return 0; }
|
||||||
var rootContainer = container.Item.GetRootContainer() ?? container.Item;
|
var rootContainer = container.Item.RootContainer ?? container.Item;
|
||||||
if (rootContainer.GetComponent<Fabricator>() != null || rootContainer.GetComponent<Deconstructor>() != null) { return 0; }
|
if (rootContainer.GetComponent<Fabricator>() != null || rootContainer.GetComponent<Deconstructor>() != null) { return 0; }
|
||||||
if (container.ShouldBeContained(containableItem, out bool isRestrictionsDefined))
|
if (container.ShouldBeContained(containableItem, out bool isRestrictionsDefined))
|
||||||
{
|
{
|
||||||
@@ -1145,7 +1154,7 @@ namespace Barotrauma
|
|||||||
string msgId = "DialogLowOxygen";
|
string msgId = "DialogLowOxygen";
|
||||||
Character.Speak(TextManager.Get(msgId).Value, delay: Rand.Range(minDelay, maxDelay), identifier: msgId.ToIdentifier(), minDurationBetweenSimilar: 30.0f);
|
Character.Speak(TextManager.Get(msgId).Value, delay: Rand.Range(minDelay, maxDelay), identifier: msgId.ToIdentifier(), minDurationBetweenSimilar: 30.0f);
|
||||||
}
|
}
|
||||||
if (Character.Bleeding > 2.0f && !Character.IsMedic)
|
if (Character.Bleeding > AfflictionPrefab.Bleeding.TreatmentThreshold && !Character.IsMedic)
|
||||||
{
|
{
|
||||||
string msgId = "DialogBleeding";
|
string msgId = "DialogBleeding";
|
||||||
Character.Speak(TextManager.Get(msgId).Value, delay: Rand.Range(minDelay, maxDelay), identifier: msgId.ToIdentifier(), minDurationBetweenSimilar: 30.0f);
|
Character.Speak(TextManager.Get(msgId).Value, delay: Rand.Range(minDelay, maxDelay), identifier: msgId.ToIdentifier(), minDurationBetweenSimilar: 30.0f);
|
||||||
@@ -1658,7 +1667,7 @@ namespace Barotrauma
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool HasDivingSuit(Character character, float conditionPercentage = 0, bool requireOxygenTank = true)
|
public static bool HasDivingSuit(Character character, float conditionPercentage = 0, bool requireOxygenTank = true)
|
||||||
=> HasItem(character, AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR, out _, requireOxygenTank ? AIObjectiveFindDivingGear.OXYGEN_SOURCE : Identifier.Empty, conditionPercentage, requireEquipped: true,
|
=> HasItem(character, AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR, out _, requireOxygenTank ? AIObjectiveFindDivingGear.OXYGEN_SOURCE : Identifier.Empty, conditionPercentage, requireEquipped: true,
|
||||||
predicate: (Item item) => character.HasEquippedItem(item, InvSlotType.OuterClothes));
|
predicate: (Item item) => character.HasEquippedItem(item, InvSlotType.OuterClothes | InvSlotType.InnerClothes));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check whether the character has a diving mask in usable condition plus some oxygen.
|
/// Check whether the character has a diving mask in usable condition plus some oxygen.
|
||||||
@@ -1891,7 +1900,7 @@ namespace Barotrauma
|
|||||||
private static float GetReactionTime() => reactionTime * Rand.Range(0.75f, 1.25f);
|
private static float GetReactionTime() => reactionTime * Rand.Range(0.75f, 1.25f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the hull safety for all ai characters in the team. The idea is that the crew communicates (magically) via radio about the threads.
|
/// Updates the hull safety for all ai characters in the team. The idea is that the crew communicates (magically) via radio about the threats.
|
||||||
/// The safety levels need to be calculated for each bot individually, because the formula takes into account things like current orders.
|
/// The safety levels need to be calculated for each bot individually, because the formula takes into account things like current orders.
|
||||||
/// There's now a cached value per each hull, which should prevent too frequent calculations.
|
/// There's now a cached value per each hull, which should prevent too frequent calculations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1900,9 +1909,13 @@ namespace Barotrauma
|
|||||||
DoForEachBot(character, (humanAi) => humanAi.RefreshHullSafety(hull));
|
DoForEachBot(character, (humanAi) => humanAi.RefreshHullSafety(hull));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AskToRecalculateHullSafety(Hull hull) => dirtyHullSafetyCalculations.Add(hull);
|
||||||
|
|
||||||
private void RefreshHullSafety(Hull hull)
|
private void RefreshHullSafety(Hull hull)
|
||||||
{
|
{
|
||||||
if (GetHullSafety(hull, Character, VisibleHulls) > HULL_SAFETY_THRESHOLD)
|
var visibleHulls = dirtyHullSafetyCalculations.Contains(hull) ? hull.GetConnectedHulls(includingThis: true, searchDepth: 1) : VisibleHulls;
|
||||||
|
float hullSafety = GetHullSafety(hull, Character, visibleHulls);
|
||||||
|
if (hullSafety > HULL_SAFETY_THRESHOLD)
|
||||||
{
|
{
|
||||||
UnsafeHulls.Remove(hull);
|
UnsafeHulls.Remove(hull);
|
||||||
}
|
}
|
||||||
|
|||||||
+123
-115
@@ -22,7 +22,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private readonly Character character;
|
private readonly Character character;
|
||||||
|
|
||||||
private Vector2 currentTarget;
|
/// <summary>
|
||||||
|
/// In sim units.
|
||||||
|
/// </summary>
|
||||||
|
private Vector2 currentTargetPos;
|
||||||
|
|
||||||
private float findPathTimer;
|
private float findPathTimer;
|
||||||
|
|
||||||
@@ -40,11 +43,6 @@ namespace Barotrauma
|
|||||||
get { return pathFinder; }
|
get { return pathFinder; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2 CurrentTarget
|
|
||||||
{
|
|
||||||
get { return currentTarget; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPathDirty
|
public bool IsPathDirty
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
@@ -54,9 +52,9 @@ namespace Barotrauma
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if any node in the path is in stairs
|
/// Returns true if any node in the path is in stairs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool InStairs => currentPath != null && currentPath.Nodes.Any(n => n.Stairs != null);
|
public bool PathHasStairs => currentPath != null && currentPath.Nodes.Any(n => n.Stairs != null);
|
||||||
|
|
||||||
public bool IsCurrentNodeLadder => currentPath?.CurrentNode?.Ladders != null && currentPath.CurrentNode.Ladders.Item.IsInteractable(character);
|
public bool IsCurrentNodeLadder => GetCurrentLadder() != null;
|
||||||
|
|
||||||
public bool IsNextNodeLadder => GetNextLadder() != null;
|
public bool IsNextNodeLadder => GetNextLadder() != null;
|
||||||
|
|
||||||
@@ -64,14 +62,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (currentPath == null) { return false; }
|
var currentLadder = GetCurrentLadder();
|
||||||
if (currentPath.CurrentNode == null) { return false; }
|
|
||||||
if (currentPath.NextNode == null) { return false; }
|
|
||||||
var currentLadder = currentPath.CurrentNode.Ladders;
|
|
||||||
if (currentLadder == null) { return false; }
|
if (currentLadder == null) { return false; }
|
||||||
if (!currentLadder.Item.IsInteractable(character)) { return false; }
|
return currentLadder == GetNextLadder();
|
||||||
var nextLadder = GetNextLadder();
|
|
||||||
return nextLadder != null && nextLadder == currentLadder;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,13 +100,10 @@ namespace Barotrauma
|
|||||||
findPathTimer -= step;
|
findPathTimer -= step;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPath(SteeringPath path)
|
public void SetPath(Vector2 targetPos, SteeringPath path)
|
||||||
{
|
{
|
||||||
|
currentTargetPos = targetPos;
|
||||||
currentPath = path;
|
currentPath = path;
|
||||||
if (path.Nodes.Any())
|
|
||||||
{
|
|
||||||
currentTarget = path.Nodes[path.Nodes.Count - 1].SimPosition;
|
|
||||||
}
|
|
||||||
findPathTimer = Math.Min(findPathTimer, 1.0f);
|
findPathTimer = Math.Min(findPathTimer, 1.0f);
|
||||||
IsPathDirty = false;
|
IsPathDirty = false;
|
||||||
}
|
}
|
||||||
@@ -136,46 +126,17 @@ namespace Barotrauma
|
|||||||
steering += addition;
|
steering += addition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public Ladder GetCurrentLadder() => GetLadder(currentPath?.CurrentNode);
|
||||||
/// Seeks the ladder from the next and next + 1 nodes.
|
|
||||||
/// </summary>
|
|
||||||
public Ladder GetNextLadder()
|
|
||||||
{
|
|
||||||
if (currentPath == null) { return null; }
|
|
||||||
if (currentPath.NextNode == null) { return null; }
|
|
||||||
if (currentPath.NextNode.Ladders != null && currentPath.NextNode.Ladders.Item.IsInteractable(character))
|
|
||||||
{
|
|
||||||
return currentPath.NextNode.Ladders;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int index = currentPath.CurrentIndex + 2;
|
|
||||||
if (currentPath.Nodes.Count > index)
|
|
||||||
{
|
|
||||||
var node = currentPath.Nodes[index];
|
|
||||||
if (node == null) { return null; }
|
|
||||||
if (node.Ladders != null && node.Ladders.Item.IsInteractable(character))
|
|
||||||
{
|
|
||||||
return node.Ladders;
|
|
||||||
}
|
|
||||||
//if the next node is a hatch, check if the node after that is a ladder
|
|
||||||
else if (node.ConnectedDoor != null && node.ConnectedDoor.IsHorizontal)
|
|
||||||
{
|
|
||||||
index++;
|
|
||||||
if (currentPath.Nodes.Count > index)
|
|
||||||
{
|
|
||||||
node = currentPath.Nodes[index];
|
|
||||||
if (node == null) { return null; }
|
|
||||||
if (node.Ladders != null && node.Ladders.Item.IsInteractable(character))
|
|
||||||
{
|
|
||||||
return node.Ladders;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
public Ladder GetNextLadder() => GetLadder(currentPath?.NextNode);
|
||||||
return null;
|
|
||||||
|
private Ladder GetLadder(WayPoint wp)
|
||||||
|
{
|
||||||
|
if (wp?.Ladders?.Item is Item item && item.IsInteractable(character))
|
||||||
|
{
|
||||||
|
return wp.Ladders;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2 CalculateSteeringSeek(Vector2 target, float weight, float minGapSize = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
|
private Vector2 CalculateSteeringSeek(Vector2 target, float weight, float minGapSize = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
|
||||||
@@ -183,19 +144,10 @@ namespace Barotrauma
|
|||||||
bool needsNewPath = currentPath == null || currentPath.Unreachable || currentPath.Finished || currentPath.CurrentNode == null;
|
bool needsNewPath = currentPath == null || currentPath.Unreachable || currentPath.Finished || currentPath.CurrentNode == null;
|
||||||
if (!needsNewPath && character.Submarine != null && character.Params.PathFinderPriority > 0.5f)
|
if (!needsNewPath && character.Submarine != null && character.Params.PathFinderPriority > 0.5f)
|
||||||
{
|
{
|
||||||
Vector2 targetDiff = target - currentTarget;
|
// If the target has moved, we need a new path.
|
||||||
if (currentPath != null && currentPath.Nodes.Any() && character.Submarine != null)
|
// Different subs are already taken into account before setting the target.
|
||||||
{
|
// Triggers when either the target or we have changed subs, but only once (until the new path has been accepted).
|
||||||
//target in a different sub than where the character is now
|
Vector2 targetDiff = target - currentTargetPos;
|
||||||
//take that into account when calculating if the target has moved
|
|
||||||
Submarine currentPathSub = currentPath?.CurrentNode?.Submarine;
|
|
||||||
if (currentPathSub == character.Submarine) { currentPathSub = currentPath?.Nodes.LastOrDefault()?.Submarine; }
|
|
||||||
if (currentPathSub != character.Submarine && targetDiff.LengthSquared() > 1 && currentPathSub != null)
|
|
||||||
{
|
|
||||||
Vector2 subDiff = character.Submarine.SimPosition - currentPathSub.SimPosition;
|
|
||||||
targetDiff += subDiff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (targetDiff.LengthSquared() > 1)
|
if (targetDiff.LengthSquared() > 1)
|
||||||
{
|
{
|
||||||
needsNewPath = true;
|
needsNewPath = true;
|
||||||
@@ -205,14 +157,14 @@ namespace Barotrauma
|
|||||||
if (needsNewPath || findPathTimer < -1.0f)
|
if (needsNewPath || findPathTimer < -1.0f)
|
||||||
{
|
{
|
||||||
IsPathDirty = true;
|
IsPathDirty = true;
|
||||||
if (!needsNewPath && findPathTimer < -1)
|
if (!needsNewPath && currentPath?.CurrentNode is WayPoint wp)
|
||||||
{
|
{
|
||||||
if (character.Submarine != null && Math.Abs(character.AnimController.TargetMovement.Combine()) <= 0)
|
if (character.Submarine != null && wp.Ladders == null && wp.ConnectedDoor == null && Math.Abs(character.AnimController.TargetMovement.Combine()) <= 0)
|
||||||
{
|
{
|
||||||
// Not moving -> need a new path.
|
// Not moving -> need a new path.
|
||||||
needsNewPath = true;
|
needsNewPath = true;
|
||||||
}
|
}
|
||||||
if (character.Submarine == null && currentPath?.CurrentNode is WayPoint wp && wp.CurrentHull != null)
|
if (character.Submarine == null && wp.CurrentHull != null)
|
||||||
{
|
{
|
||||||
// Current node inside, while we are outside
|
// Current node inside, while we are outside
|
||||||
// -> Check that the current node is not too far (can happen e.g. if someone controls the character in the meanwhile)
|
// -> Check that the current node is not too far (can happen e.g. if someone controls the character in the meanwhile)
|
||||||
@@ -226,7 +178,7 @@ namespace Barotrauma
|
|||||||
if (findPathTimer < 0)
|
if (findPathTimer < 0)
|
||||||
{
|
{
|
||||||
SkipCurrentPathNodes();
|
SkipCurrentPathNodes();
|
||||||
currentTarget = target;
|
currentTargetPos = target;
|
||||||
Vector2 currentPos = host.SimPosition;
|
Vector2 currentPos = host.SimPosition;
|
||||||
pathFinder.InsideSubmarine = character.Submarine != null && !character.Submarine.Info.IsRuin;
|
pathFinder.InsideSubmarine = character.Submarine != null && !character.Submarine.Info.IsRuin;
|
||||||
pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && !character.IsProtectedFromPressure;
|
pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && !character.IsProtectedFromPressure;
|
||||||
@@ -252,6 +204,14 @@ namespace Barotrauma
|
|||||||
useNewPath = Vector2.DistanceSquared(character.WorldPosition, currentPath.CurrentNode.WorldPosition) > Math.Pow(Vector2.Distance(character.WorldPosition, newPath.Nodes.First().WorldPosition) * 3, 2);
|
useNewPath = Vector2.DistanceSquared(character.WorldPosition, currentPath.CurrentNode.WorldPosition) > Math.Pow(Vector2.Distance(character.WorldPosition, newPath.Nodes.First().WorldPosition) * 3, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!useNewPath && !character.CanSeeTarget(currentPath.CurrentNode))
|
||||||
|
{
|
||||||
|
// If we are set to disregard the new path, ensure that we can actually see the current node of the old path,
|
||||||
|
// because it's possible that there's e.g. a closed door between us and the current node,
|
||||||
|
// and in that case we'd want to use the new path instead of the old.
|
||||||
|
// There's visibility checks in the pathfinder calls, so the new path should always be ok.
|
||||||
|
useNewPath = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsIdenticalPath()
|
bool IsIdenticalPath()
|
||||||
{
|
{
|
||||||
@@ -330,6 +290,7 @@ namespace Barotrauma
|
|||||||
//if not in water and the waypoint is between the top and bottom of the collider, no need to move vertically
|
//if not in water and the waypoint is between the top and bottom of the collider, no need to move vertically
|
||||||
if (canClimb && !character.AnimController.InWater && !character.IsClimbing && diff.Y < collider.Height / 2 + collider.Radius)
|
if (canClimb && !character.AnimController.InWater && !character.IsClimbing && diff.Y < collider.Height / 2 + collider.Radius)
|
||||||
{
|
{
|
||||||
|
// TODO: might cause some edge cases -> do we need this?
|
||||||
diff.Y = 0.0f;
|
diff.Y = 0.0f;
|
||||||
}
|
}
|
||||||
if (diff == Vector2.Zero) { return Vector2.Zero; }
|
if (diff == Vector2.Zero) { return Vector2.Zero; }
|
||||||
@@ -346,12 +307,12 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
if (currentPath.Finished)
|
if (currentPath.Finished)
|
||||||
{
|
{
|
||||||
Vector2 pos2 = host.SimPosition;
|
Vector2 hostPosition = host.SimPosition;
|
||||||
if (character != null && character.Submarine == null && CurrentPath.Nodes.Count > 0 && CurrentPath.Nodes.Last().Submarine != null)
|
if (character != null && character.Submarine == null && CurrentPath.Nodes.Count > 0 && CurrentPath.Nodes.Last().Submarine != null)
|
||||||
{
|
{
|
||||||
pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition;
|
hostPosition -= CurrentPath.Nodes.Last().Submarine.SimPosition;
|
||||||
}
|
}
|
||||||
return currentTarget - pos2;
|
return currentTargetPos - hostPosition;
|
||||||
}
|
}
|
||||||
bool doorsChecked = false;
|
bool doorsChecked = false;
|
||||||
checkDoorsTimer = Math.Min(checkDoorsTimer, GetDoorCheckTime());
|
checkDoorsTimer = Math.Min(checkDoorsTimer, GetDoorCheckTime());
|
||||||
@@ -371,14 +332,46 @@ namespace Barotrauma
|
|||||||
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
|
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
|
||||||
// Only humanoids can climb ladders
|
// Only humanoids can climb ladders
|
||||||
bool canClimb = character.AnimController is HumanoidAnimController && !character.LockHands;
|
bool canClimb = character.AnimController is HumanoidAnimController && !character.LockHands;
|
||||||
Ladder currentLadder = currentPath.CurrentNode.Ladders;
|
Ladder currentLadder = GetCurrentLadder();
|
||||||
if (currentLadder != null && !currentLadder.Item.IsInteractable(character))
|
|
||||||
{
|
|
||||||
currentLadder = null;
|
|
||||||
}
|
|
||||||
Ladder nextLadder = GetNextLadder();
|
Ladder nextLadder = GetNextLadder();
|
||||||
var ladders = currentLadder ?? nextLadder;
|
var ladders = currentLadder ?? nextLadder;
|
||||||
bool useLadders = canClimb && ladders != null && steering.LengthSquared() > 0.1f && (!isDiving || steering.Y > 1);
|
bool useLadders = canClimb && ladders != null;
|
||||||
|
var collider = character.AnimController.Collider;
|
||||||
|
Vector2 colliderSize = collider.GetSize();
|
||||||
|
if (useLadders)
|
||||||
|
{
|
||||||
|
if (character.IsClimbing && Math.Abs(diff.X) - ConvertUnits.ToDisplayUnits(colliderSize.X) > Math.Abs(diff.Y))
|
||||||
|
{
|
||||||
|
// If the current node is horizontally farther from us than vertically, we don't want to keep climbing the ladders.
|
||||||
|
useLadders = false;
|
||||||
|
}
|
||||||
|
else if (!character.IsClimbing && currentPath.NextNode != null && nextLadder == null)
|
||||||
|
{
|
||||||
|
Vector2 diffToNextNode = currentPath.NextNode.WorldPosition - pos;
|
||||||
|
if (Math.Abs(diffToNextNode.X) > Math.Abs(diffToNextNode.Y))
|
||||||
|
{
|
||||||
|
// If the next node is horizontally farther from us than vertically, we don't want to start climbing.
|
||||||
|
useLadders = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isDiving && steering.Y < 1)
|
||||||
|
{
|
||||||
|
// When diving, only use ladders to get upwards (towards the surface), otherwise we can just ignore them.
|
||||||
|
useLadders = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (character.IsClimbing && !useLadders)
|
||||||
|
{
|
||||||
|
if (currentPath.IsAtEndNode && canClimb && ladders != null)
|
||||||
|
{
|
||||||
|
// Don't release the ladders when ending a path in ladders.
|
||||||
|
useLadders = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
character.StopClimbing();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (useLadders && character.SelectedSecondaryItem != ladders.Item)
|
if (useLadders && character.SelectedSecondaryItem != ladders.Item)
|
||||||
{
|
{
|
||||||
if (character.CanInteractWith(ladders.Item))
|
if (character.CanInteractWith(ladders.Item))
|
||||||
@@ -398,40 +391,28 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var collider = character.AnimController.Collider;
|
|
||||||
if (character.IsClimbing && !useLadders)
|
|
||||||
{
|
|
||||||
character.StopClimbing();
|
|
||||||
}
|
|
||||||
if (character.IsClimbing && useLadders)
|
if (character.IsClimbing && useLadders)
|
||||||
{
|
{
|
||||||
if (currentLadder == null && nextLadder != null)
|
if (currentLadder == null && nextLadder != null && character.SelectedSecondaryItem == nextLadder.Item)
|
||||||
{
|
{
|
||||||
// Climbing a ladder but the path is still on the node next to the ladder -> Skip the node.
|
// Climbing a ladder but the path is still on the node next to the ladder -> Skip the node.
|
||||||
NextNode(!doorsChecked);
|
NextNode(!doorsChecked);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent;
|
bool nextLadderSameAsCurrent = currentLadder == nextLadder;
|
||||||
if (nextLadderSameAsCurrent || currentLadder != null && nextLadder != null && Math.Abs(currentLadder.Item.Position.X - nextLadder.Item.Position.X) < 50)
|
if (currentLadder != null && nextLadder != null)
|
||||||
{
|
{
|
||||||
//climbing ladders -> don't move horizontally
|
//climbing ladders -> don't move horizontally
|
||||||
diff.X = 0.0f;
|
diff.X = 0.0f;
|
||||||
}
|
}
|
||||||
//at the same height as the waypoint
|
//at the same height as the waypoint
|
||||||
float heightDiff = Math.Abs(collider.SimPosition.Y - currentPath.CurrentNode.SimPosition.Y);
|
float heightDiff = Math.Abs(collider.SimPosition.Y - currentPath.CurrentNode.SimPosition.Y);
|
||||||
float colliderSize = (collider.Height / 2 + collider.Radius) * 1.25f;
|
float colliderHeight = collider.Height / 2 + collider.Radius;
|
||||||
if (heightDiff < colliderSize)
|
float distanceMargin = ConvertUnits.ToDisplayUnits(colliderSize.X);
|
||||||
|
if (heightDiff < colliderHeight * 1.25f)
|
||||||
{
|
{
|
||||||
float heightFromFloor = character.AnimController.GetHeightFromFloor();
|
if (nextLadder != null && !nextLadderSameAsCurrent)
|
||||||
// We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative.
|
|
||||||
bool isAboveFloor = heightFromFloor > -0.1f;
|
|
||||||
// If the next waypoint is horizontally far, we don't want to keep holding the ladders
|
|
||||||
if (isAboveFloor && !currentPath.IsAtEndNode && (nextLadder == null || Math.Abs(currentPath.CurrentNode.WorldPosition.X - currentPath.NextNode.WorldPosition.X) > 50))
|
|
||||||
{
|
|
||||||
character.StopClimbing();
|
|
||||||
}
|
|
||||||
else if (nextLadder != null && !nextLadderSameAsCurrent)
|
|
||||||
{
|
{
|
||||||
// Try to change the ladder (hatches between two submarines)
|
// Try to change the ladder (hatches between two submarines)
|
||||||
if (character.SelectedSecondaryItem != nextLadder.Item && character.CanInteractWith(nextLadder.Item))
|
if (character.SelectedSecondaryItem != nextLadder.Item && character.CanInteractWith(nextLadder.Item))
|
||||||
@@ -442,12 +423,36 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isAboveFloor || nextLadderSameAsCurrent || nextLadder == null && Math.Abs(diff.Y) < 10)
|
bool isAboveFloor;
|
||||||
|
if (diff.Y < 0)
|
||||||
{
|
{
|
||||||
NextNode(!doorsChecked);
|
// When climbing down, let's use the collider bottom to prevent getting stuck at the bottom of the ladders.
|
||||||
|
float colliderBottom = character.AnimController.Collider.SimPosition.Y;
|
||||||
|
float floorY = character.AnimController.FloorY;
|
||||||
|
isAboveFloor = colliderBottom > floorY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// When climbing up, let's use the lowest collider (feet).
|
||||||
|
// We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative,
|
||||||
|
// when a foot is still below the platform.
|
||||||
|
float heightFromFloor = character.AnimController.GetHeightFromFloor();
|
||||||
|
isAboveFloor = heightFromFloor > -0.1f;
|
||||||
|
}
|
||||||
|
if (isAboveFloor)
|
||||||
|
{
|
||||||
|
if (Math.Abs(diff.Y) < distanceMargin)
|
||||||
|
{
|
||||||
|
NextNode(!doorsChecked);
|
||||||
|
}
|
||||||
|
else if (!currentPath.IsAtEndNode && (nextLadder == null || (currentLadder != null && Math.Abs(currentLadder.Item.WorldPosition.X - nextLadder.Item.WorldPosition.X) > distanceMargin)))
|
||||||
|
{
|
||||||
|
// Can't skip the node -> Release the ladders, because the next node is not on a ladder or it's horizontally too far.
|
||||||
|
character.StopClimbing();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (nextLadder != null)
|
else if (currentLadder != null && currentPath.NextNode != null)
|
||||||
{
|
{
|
||||||
if (Math.Sign(currentPath.CurrentNode.WorldPosition.Y - character.WorldPosition.Y) != Math.Sign(currentPath.NextNode.WorldPosition.Y - character.WorldPosition.Y))
|
if (Math.Sign(currentPath.CurrentNode.WorldPosition.Y - character.WorldPosition.Y) != Math.Sign(currentPath.NextNode.WorldPosition.Y - character.WorldPosition.Y))
|
||||||
{
|
{
|
||||||
@@ -466,7 +471,6 @@ namespace Barotrauma
|
|||||||
if (door == null || door.CanBeTraversed)
|
if (door == null || door.CanBeTraversed)
|
||||||
{
|
{
|
||||||
float margin = MathHelper.Lerp(1, 5, MathHelper.Clamp(collider.LinearVelocity.Length() / 10, 0, 1));
|
float margin = MathHelper.Lerp(1, 5, MathHelper.Clamp(collider.LinearVelocity.Length() / 10, 0, 1));
|
||||||
Vector2 colliderSize = collider.GetSize();
|
|
||||||
float targetDistance = Math.Max(Math.Max(colliderSize.X, colliderSize.Y) / 2 * margin, 0.5f);
|
float targetDistance = Math.Max(Math.Max(colliderSize.X, colliderSize.Y) / 2 * margin, 0.5f);
|
||||||
float horizontalDistance = Math.Abs(character.WorldPosition.X - currentPath.CurrentNode.WorldPosition.X);
|
float horizontalDistance = Math.Abs(character.WorldPosition.X - currentPath.CurrentNode.WorldPosition.X);
|
||||||
float verticalDistance = Math.Abs(character.WorldPosition.Y - currentPath.CurrentNode.WorldPosition.Y);
|
float verticalDistance = Math.Abs(character.WorldPosition.Y - currentPath.CurrentNode.WorldPosition.Y);
|
||||||
@@ -485,7 +489,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
// Walking horizontally
|
// Walking horizontally
|
||||||
Vector2 colliderBottom = character.AnimController.GetColliderBottom();
|
Vector2 colliderBottom = character.AnimController.GetColliderBottom();
|
||||||
Vector2 colliderSize = collider.GetSize();
|
|
||||||
Vector2 velocity = collider.LinearVelocity;
|
Vector2 velocity = collider.LinearVelocity;
|
||||||
// If the character is very short, it would fail to use the waypoint nodes because they are always too high.
|
// If the character is very short, it would fail to use the waypoint nodes because they are always too high.
|
||||||
// If the character is very thin, it would often fail to reach the waypoints, because the horizontal distance is too small.
|
// If the character is very thin, it would often fail to reach the waypoints, because the horizontal distance is too small.
|
||||||
@@ -512,9 +515,12 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
float targetDistance = Math.Max(colliderSize.X / 2 * margin, minWidth / 2);
|
float targetDistance = Math.Max(colliderSize.X / 2 * margin, minWidth / 2);
|
||||||
if (horizontalDistance < targetDistance && !isTargetTooHigh && !isTargetTooLow && currentLadder == null && (door == null || door.CanBeTraversed))
|
if (horizontalDistance < targetDistance && !isTargetTooHigh && !isTargetTooLow)
|
||||||
{
|
{
|
||||||
NextNode(!doorsChecked);
|
if (door is not { CanBeTraversed: false } && (currentLadder == null || nextLadder == null))
|
||||||
|
{
|
||||||
|
NextNode(!doorsChecked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentPath.CurrentNode == null)
|
if (currentPath.CurrentNode == null)
|
||||||
@@ -533,9 +539,9 @@ namespace Barotrauma
|
|||||||
currentPath.SkipToNextNode();
|
currentPath.SkipToNextNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanAccessDoor(Door door, Func<Controller, bool> buttonFilter = null)
|
public bool CanAccessDoor(Door door, Func<Controller, bool> buttonFilter = null)
|
||||||
{
|
{
|
||||||
if (door.IsBroken) { return true; }
|
if (door.CanBeTraversed) { return true; }
|
||||||
if (door.IsClosed)
|
if (door.IsClosed)
|
||||||
{
|
{
|
||||||
if (!door.Item.IsInteractable(character)) { return false; }
|
if (!door.Item.IsInteractable(character)) { return false; }
|
||||||
@@ -631,10 +637,12 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
//the node we're heading towards is the last one in the path, and at a door
|
//the node we're heading towards is the last one in the path, and at a door
|
||||||
//the door needs to be open for the character to reach the node
|
//the door needs to be open for the character to reach the node
|
||||||
if (currentWaypoint.ConnectedDoor.LinkedGap != null)
|
if (currentWaypoint.ConnectedDoor.LinkedGap is Gap linkedGap)
|
||||||
{
|
{
|
||||||
// Keep the airlock doors closed, but not in ruins/wrecks
|
if (currentWaypoint.Submarine == null ||
|
||||||
if (currentWaypoint.ConnectedDoor.LinkedGap.IsRoomToRoom && currentWaypoint.CurrentHull is { IsWetRoom: false } || currentWaypoint.Submarine == null || currentWaypoint.Submarine.Info.IsRuin || currentWaypoint.Submarine.Info.IsWreck)
|
currentWaypoint.Submarine.Info is { IsPlayer: false } ||
|
||||||
|
!linkedGap.IsRoomToRoom ||
|
||||||
|
(linkedGap.IsRoomToRoom && currentWaypoint.CurrentHull is { IsWetRoom: false }))
|
||||||
{
|
{
|
||||||
shouldBeOpen = true;
|
shouldBeOpen = true;
|
||||||
door = currentWaypoint.ConnectedDoor;
|
door = currentWaypoint.ConnectedDoor;
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (Voronoi2.GraphEdge edge in cell.Edges)
|
foreach (Voronoi2.GraphEdge edge in cell.Edges)
|
||||||
{
|
{
|
||||||
if (MathUtils.GetLineIntersection(edge.Point1, edge.Point2, character.WorldPosition, cell.Center, out Vector2 intersection))
|
if (MathUtils.GetLineSegmentIntersection(edge.Point1, edge.Point2, character.WorldPosition, cell.Center, out Vector2 intersection))
|
||||||
{
|
{
|
||||||
Vector2 potentialAttachPos = ConvertUnits.ToSimUnits(intersection);
|
Vector2 potentialAttachPos = ConvertUnits.ToSimUnits(intersection);
|
||||||
float distSqr = Vector2.DistanceSquared(character.SimPosition, potentialAttachPos);
|
float distSqr = Vector2.DistanceSquared(character.SimPosition, potentialAttachPos);
|
||||||
|
|||||||
@@ -506,6 +506,8 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void SpeakAfterOrderReceived() { }
|
||||||
|
|
||||||
protected static bool CanEquip(Character character, Item item, bool allowWearing)
|
protected static bool CanEquip(Character character, Item item, bool allowWearing)
|
||||||
{
|
{
|
||||||
if (item == null) { return false; }
|
if (item == null) { return false; }
|
||||||
|
|||||||
+6
-2
@@ -14,6 +14,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public readonly List<Item> prioritizedItems = new List<Item>();
|
public readonly List<Item> prioritizedItems = new List<Item>();
|
||||||
|
|
||||||
|
public static readonly Identifier AllowCleanupTag = "allowcleanup".ToIdentifier();
|
||||||
|
|
||||||
|
protected override int MaxTargets => 100;
|
||||||
|
|
||||||
public AIObjectiveCleanupItems(Character character, AIObjectiveManager objectiveManager, Item prioritizedItem = null, float priorityModifier = 1)
|
public AIObjectiveCleanupItems(Character character, AIObjectiveManager objectiveManager, Item prioritizedItem = null, float priorityModifier = 1)
|
||||||
: base(character, objectiveManager, priorityModifier)
|
: base(character, objectiveManager, priorityModifier)
|
||||||
{
|
{
|
||||||
@@ -81,8 +85,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public static bool IsValidContainer(Item container, Character character, bool allowUnloading = true) =>
|
public static bool IsValidContainer(Item container, Character character, bool allowUnloading = true) =>
|
||||||
allowUnloading &&
|
allowUnloading &&
|
||||||
|
container.HasTag(AllowCleanupTag) &&
|
||||||
container.HasAccess(character) &&
|
container.HasAccess(character) &&
|
||||||
container.HasTag("allowcleanup") &&
|
|
||||||
container.ParentInventory == null && container.OwnInventory != null && container.OwnInventory.AllItems.Any() &&
|
container.ParentInventory == null && container.OwnInventory != null && container.OwnInventory.AllItems.Any() &&
|
||||||
container.GetComponent<ItemContainer>() != null &&
|
container.GetComponent<ItemContainer>() != null &&
|
||||||
IsItemInsideValidSubmarine(container, character) &&
|
IsItemInsideValidSubmarine(container, character) &&
|
||||||
@@ -91,7 +95,6 @@ namespace Barotrauma
|
|||||||
public static bool IsValidTarget(Item item, Character character, bool checkInventory, bool allowUnloading = true)
|
public static bool IsValidTarget(Item item, Character character, bool checkInventory, bool allowUnloading = true)
|
||||||
{
|
{
|
||||||
if (item == null) { return false; }
|
if (item == null) { return false; }
|
||||||
if (!item.HasAccess(character)) { return false; }
|
|
||||||
if ((item.SpawnedInCurrentOutpost && !item.AllowStealing) == character.IsOnPlayerTeam) { return false; }
|
if ((item.SpawnedInCurrentOutpost && !item.AllowStealing) == character.IsOnPlayerTeam) { return false; }
|
||||||
if (item.ParentInventory != null)
|
if (item.ParentInventory != null)
|
||||||
{
|
{
|
||||||
@@ -102,6 +105,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
if (!IsValidContainer(item.Container, character, allowUnloading)) { return false; }
|
if (!IsValidContainer(item.Container, character, allowUnloading)) { return false; }
|
||||||
}
|
}
|
||||||
|
if (!item.HasAccess(character)) { return false; }
|
||||||
if (character != null && !IsItemInsideValidSubmarine(item, character)) { return false; }
|
if (character != null && !IsItemInsideValidSubmarine(item, character)) { return false; }
|
||||||
if (item.HasBallastFloraInHull) { return false; }
|
if (item.HasBallastFloraInHull) { return false; }
|
||||||
var wire = item.GetComponent<Wire>();
|
var wire = item.GetComponent<Wire>();
|
||||||
|
|||||||
+10
-2
@@ -995,10 +995,18 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (HumanAIController.HasItem(character, "handlocker".ToIdentifier(), out IEnumerable<Item> matchingItems) && !Enemy.IsUnconscious && Enemy.IsKnockedDown && character.CanInteractWith(Enemy))
|
|
||||||
|
//prefer using handcuffs already on the enemy's inventory
|
||||||
|
if (!HumanAIController.HasItem(Enemy, "handlocker".ToIdentifier(), out IEnumerable<Item> matchingItems))
|
||||||
|
{
|
||||||
|
HumanAIController.HasItem(character, "handlocker".ToIdentifier(), out matchingItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingItems.Any() &&
|
||||||
|
!Enemy.IsUnconscious && Enemy.IsKnockedDown && character.CanInteractWith(Enemy) && !Enemy.LockHands)
|
||||||
{
|
{
|
||||||
var handCuffs = matchingItems.First();
|
var handCuffs = matchingItems.First();
|
||||||
if (!HumanAIController.TakeItem(handCuffs, Enemy.Inventory, equip: true))
|
if (!HumanAIController.TakeItem(handCuffs, Enemy.Inventory, equip: true, wear: true))
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
DebugConsole.NewMessage($"{character.Name}: Failed to handcuff the target.", Color.Red);
|
DebugConsole.NewMessage($"{character.Name}: Failed to handcuff the target.", Color.Red);
|
||||||
|
|||||||
+1
-1
@@ -198,7 +198,7 @@ namespace Barotrauma
|
|||||||
TargetName = container.Item.Name,
|
TargetName = container.Item.Name,
|
||||||
AbortCondition = obj =>
|
AbortCondition = obj =>
|
||||||
container?.Item == null || container.Item.Removed || !container.Item.HasAccess(character) ||
|
container?.Item == null || container.Item.Removed || !container.Item.HasAccess(character) ||
|
||||||
(container.Item.GetRootContainer()?.OwnInventory?.Locked ?? false) ||
|
(container.Item.RootContainer?.OwnInventory?.Locked ?? false) ||
|
||||||
ItemToContain == null || ItemToContain.Removed ||
|
ItemToContain == null || ItemToContain.Removed ||
|
||||||
!ItemToContain.IsOwnedBy(character) || container.Item.GetRootInventoryOwner() is Character c && c != character,
|
!ItemToContain.IsOwnedBy(character) || container.Item.GetRootInventoryOwner() is Character c && c != character,
|
||||||
SpeakIfFails = !objectiveManager.IsCurrentOrder<AIObjectiveCleanupItems>(),
|
SpeakIfFails = !objectiveManager.IsCurrentOrder<AIObjectiveCleanupItems>(),
|
||||||
|
|||||||
+4
-3
@@ -30,7 +30,8 @@ namespace Barotrauma
|
|||||||
public static readonly Identifier DIVING_GEAR_WEARABLE_INDOORS = "divinggear_wearableindoors".ToIdentifier();
|
public static readonly Identifier DIVING_GEAR_WEARABLE_INDOORS = "divinggear_wearableindoors".ToIdentifier();
|
||||||
public static readonly Identifier OXYGEN_SOURCE = "oxygensource".ToIdentifier();
|
public static readonly Identifier OXYGEN_SOURCE = "oxygensource".ToIdentifier();
|
||||||
|
|
||||||
protected override bool CheckObjectiveSpecific() => targetItem != null && character.HasEquippedItem(targetItem, slotType: InvSlotType.OuterClothes | InvSlotType.Head);
|
protected override bool CheckObjectiveSpecific() =>
|
||||||
|
targetItem != null && character.HasEquippedItem(targetItem, slotType: InvSlotType.OuterClothes | InvSlotType.InnerClothes | InvSlotType.Head);
|
||||||
|
|
||||||
public AIObjectiveFindDivingGear(Character character, bool needsDivingSuit, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier)
|
public AIObjectiveFindDivingGear(Character character, bool needsDivingSuit, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier)
|
||||||
{
|
{
|
||||||
@@ -51,7 +52,7 @@ namespace Barotrauma
|
|||||||
TrySetTargetItem(character.Inventory.FindItemByTag(HEAVY_DIVING_GEAR, true));
|
TrySetTargetItem(character.Inventory.FindItemByTag(HEAVY_DIVING_GEAR, true));
|
||||||
}
|
}
|
||||||
if (targetItem == null ||
|
if (targetItem == null ||
|
||||||
!character.HasEquippedItem(targetItem, slotType: InvSlotType.OuterClothes | InvSlotType.Head | InvSlotType.InnerClothes) &&
|
!character.HasEquippedItem(targetItem, slotType: InvSlotType.OuterClothes | InvSlotType.InnerClothes | InvSlotType.Head) &&
|
||||||
targetItem.ContainedItems.Any(it => IsSuitableContainedOxygenSource(it)))
|
targetItem.ContainedItems.Any(it => IsSuitableContainedOxygenSource(it)))
|
||||||
{
|
{
|
||||||
TryAddSubObjective(ref getDivingGear, () =>
|
TryAddSubObjective(ref getDivingGear, () =>
|
||||||
@@ -65,7 +66,7 @@ namespace Barotrauma
|
|||||||
AllowStealing = HumanAIController.NeedsDivingGear(character.CurrentHull, out _),
|
AllowStealing = HumanAIController.NeedsDivingGear(character.CurrentHull, out _),
|
||||||
AllowToFindDivingGear = false,
|
AllowToFindDivingGear = false,
|
||||||
AllowDangerousPressure = true,
|
AllowDangerousPressure = true,
|
||||||
EquipSlotType = InvSlotType.OuterClothes | InvSlotType.Head | InvSlotType.InnerClothes,
|
EquipSlotType = InvSlotType.OuterClothes | InvSlotType.InnerClothes | InvSlotType.Head,
|
||||||
Wear = true
|
Wear = true
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user