Merge remote-tracking branch 'origin/master' into dev
# Conflicts: # Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs # Barotrauma/BarotraumaClient/ClientSource/Events/EventManager.cs # Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs # Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs # Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs # Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs # Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs # Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs # Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs # Barotrauma/BarotraumaClient/ClientSource/Items/Components/Door.cs # Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs # Barotrauma/BarotraumaClient/ClientSource/Items/Components/Projectile.cs # Barotrauma/BarotraumaClient/ClientSource/Map/Lights/ConvexHull.cs # Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs # Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightSource.cs # Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs # Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs # Barotrauma/BarotraumaClient/ClientSource/Networking/Voip/VoipClient.cs # Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs # Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen/ServerListScreen.cs # Barotrauma/BarotraumaClient/LinuxClient.csproj # Barotrauma/BarotraumaClient/MacClient.csproj # Barotrauma/BarotraumaClient/WindowsClient.csproj # Barotrauma/BarotraumaServer/LinuxServer.csproj # Barotrauma/BarotraumaServer/MacServer.csproj # Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs # Barotrauma/BarotraumaServer/ServerSource/GameSession/MedicalClinic.cs # Barotrauma/BarotraumaServer/ServerSource/Items/Inventory.cs # Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs # Barotrauma/BarotraumaServer/ServerSource/Utils/DoSProtection.cs # Barotrauma/BarotraumaServer/WindowsServer.csproj # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveContainItem.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindSafety.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveIdle.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/ShipCommand/ShipIssueWorkerOperateWeapons.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/Affliction.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs # Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs # Barotrauma/BarotraumaShared/SharedSource/Enums.cs # Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/MissionAction.cs # Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCChangeTeamAction.cs # Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCFollowAction.cs # Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCWaitAction.cs # Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs # Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs # Barotrauma/BarotraumaShared/SharedSource/Events/Missions/PirateMission.cs # Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs # Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs # Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs # Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs # Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Door.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/LevelResource.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/LightComponent.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Turret.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs # Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs # Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineBody.cs # Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs # Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs # Barotrauma/BarotraumaShared/SharedSource/StatusEffects/PropertyConditional.cs # Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs # Barotrauma/BarotraumaShared/changelog.txt
This commit is contained in:
@@ -19,7 +19,7 @@ namespace Barotrauma
|
||||
var target = _selectedAiTarget ?? _lastAiTarget;
|
||||
if (target != null && target.Entity != null)
|
||||
{
|
||||
var memory = GetTargetMemory(target, false);
|
||||
var memory = GetTargetMemory(target);
|
||||
if (memory != null)
|
||||
{
|
||||
Vector2 targetPos = memory.Location;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Barotrauma
|
||||
public override void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
|
||||
{
|
||||
if (Character == Character.Controlled) { return; }
|
||||
if (!debugai) { return; }
|
||||
if (!DebugAI) { return; }
|
||||
Vector2 pos = Character.WorldPosition;
|
||||
pos.Y = -pos.Y;
|
||||
Vector2 textOffset = new Vector2(-40, -160);
|
||||
|
||||
@@ -6,11 +6,17 @@ namespace Barotrauma
|
||||
{
|
||||
partial class Attack
|
||||
{
|
||||
[Serialize("StructureBlunt", IsPropertySaveable.Yes), Editable()]
|
||||
[Serialize("StructureBlunt", IsPropertySaveable.Yes, description: "Name of the sound effect the attack makes when it hits a structure."), Editable()]
|
||||
public string StructureSoundType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sound to play when the attack deals damage.
|
||||
/// </summary>
|
||||
private RoundSound sound;
|
||||
|
||||
/// <summary>
|
||||
/// Particle emitter to use when the attack deals damage.
|
||||
/// </summary>
|
||||
private ParticleEmitter particleEmitter;
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
|
||||
@@ -582,7 +582,7 @@ namespace Barotrauma
|
||||
float closestItemDistance = Math.Max(aimAssistAmount, 2.0f);
|
||||
foreach (MapEntity entity in entityList)
|
||||
{
|
||||
if (!(entity is Item item))
|
||||
if (entity is not Item item)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -214,7 +214,6 @@ namespace Barotrauma
|
||||
|
||||
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
|
||||
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
|
||||
TransformCursorPos();
|
||||
|
||||
bool ragdollInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Ragdoll].Held = ragdollInput;
|
||||
|
||||
@@ -850,7 +850,7 @@ namespace Barotrauma
|
||||
if (treatmentButton.Enabled && treatmentButton.State == GUIComponent.ComponentState.Hover)
|
||||
{
|
||||
//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);
|
||||
if (Character.Controlled.Inventory.visualSlots != null && index > -1 && index < Character.Controlled.Inventory.visualSlots.Length &&
|
||||
Character.Controlled.Inventory.visualSlots[index].HighlightTimer <= 0.0f)
|
||||
|
||||
@@ -870,7 +870,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (wearable.Type == WearableType.Hair)
|
||||
{
|
||||
if (HairWithHatSprite != null)
|
||||
if (HairWithHatSprite != null && !hideLimb)
|
||||
{
|
||||
DrawWearable(HairWithHatSprite, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
||||
depthStep += step;
|
||||
|
||||
+4
-2
@@ -86,7 +86,7 @@ namespace Barotrauma
|
||||
|
||||
public string ModVersion = ContentPackage.DefaultModVersion;
|
||||
|
||||
public Md5Hash? ExpectedHash { get; private set; }
|
||||
public Md5Hash? ExpectedHash { get; set; }
|
||||
|
||||
public bool IsCore = false;
|
||||
|
||||
@@ -125,9 +125,11 @@ namespace Barotrauma
|
||||
|
||||
public static string IncrementModVersion(string modVersion)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(modVersion)) { return string.Empty; }
|
||||
|
||||
//look for an integer at the end of the string and increment it
|
||||
int startIndex = modVersion.Length - 1;
|
||||
while (char.IsDigit(modVersion[startIndex])) { startIndex--; }
|
||||
while (startIndex > 0 && char.IsDigit(modVersion[startIndex])) { startIndex--; }
|
||||
startIndex++;
|
||||
|
||||
if (startIndex >= modVersion.Length
|
||||
|
||||
@@ -9,6 +9,7 @@ using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -636,15 +637,29 @@ namespace Barotrauma
|
||||
commands.Add(new Command("wikiimage_character", "Save an image of the currently controlled character with a transparent background.", (string[] args) =>
|
||||
{
|
||||
if (Character.Controlled == null) { return; }
|
||||
WikiImage.Create(Character.Controlled);
|
||||
try
|
||||
{
|
||||
WikiImage.Create(Character.Controlled);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("The command 'wikiimage_character' failed.", e);
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("wikiimage_sub", "Save an image of the main submarine with a transparent background.", (string[] args) =>
|
||||
{
|
||||
if (Submarine.MainSub == null) { return; }
|
||||
MapEntity.SelectedList.Clear();
|
||||
MapEntity.ClearHighlightedEntities();
|
||||
WikiImage.Create(Submarine.MainSub);
|
||||
try
|
||||
{
|
||||
MapEntity.SelectedList.Clear();
|
||||
MapEntity.ClearHighlightedEntities();
|
||||
WikiImage.Create(Submarine.MainSub);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("The command 'wikiimage_sub' failed.", e);
|
||||
}
|
||||
}));
|
||||
|
||||
AssignRelayToServer("kick", false);
|
||||
@@ -1141,6 +1156,15 @@ namespace Barotrauma
|
||||
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) =>
|
||||
@@ -1263,8 +1287,8 @@ namespace Barotrauma
|
||||
|
||||
AssignOnExecute("debugai", (string[] args) =>
|
||||
{
|
||||
HumanAIController.debugai = !HumanAIController.debugai;
|
||||
if (HumanAIController.debugai)
|
||||
HumanAIController.DebugAI = !HumanAIController.DebugAI;
|
||||
if (HumanAIController.DebugAI)
|
||||
{
|
||||
GameMain.DevMode = true;
|
||||
GameMain.DebugDraw = true;
|
||||
@@ -1279,7 +1303,7 @@ namespace Barotrauma
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
GameMain.LightManager.LosAlpha = 1f;
|
||||
}
|
||||
NewMessage(HumanAIController.debugai ? "AI debug info visible" : "AI debug info hidden", Color.Yellow);
|
||||
NewMessage(HumanAIController.DebugAI ? "AI debug info visible" : "AI debug info hidden", Color.Yellow);
|
||||
});
|
||||
AssignRelayToServer("debugai", false);
|
||||
|
||||
@@ -2338,7 +2362,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (mapEntity is Item item)
|
||||
{
|
||||
item.Rect = new Rectangle(item.Rect.X, item.Rect.Y,
|
||||
item.Rect = item.DefaultRect = new Rectangle(item.Rect.X, item.Rect.Y,
|
||||
(int)(item.Prefab.Sprite.size.X * item.Prefab.Scale),
|
||||
(int)(item.Prefab.Sprite.size.Y * item.Prefab.Scale));
|
||||
}
|
||||
@@ -2811,7 +2835,26 @@ namespace Barotrauma
|
||||
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) =>
|
||||
{
|
||||
ContentPackage.IngameModSwap = !ContentPackage.IngameModSwap;
|
||||
|
||||
@@ -662,34 +662,36 @@ namespace Barotrauma
|
||||
Identifier missionIdentifier = msg.ReadIdentifier();
|
||||
int locationIndex = msg.ReadInt32();
|
||||
int destinationIndex = msg.ReadInt32();
|
||||
|
||||
string missionName = msg.ReadString();
|
||||
MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier);
|
||||
if (prefab != null)
|
||||
if (Screen.Selected != GameMain.NetLobbyScreen)
|
||||
{
|
||||
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))
|
||||
MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier);
|
||||
if (prefab != null)
|
||||
{
|
||||
IconColor = prefab.IconColor
|
||||
};
|
||||
if (GameMain.GameSession?.Map is { } map && locationIndex >= 0 && locationIndex < map.Locations.Count)
|
||||
{
|
||||
Location location = map.Locations[locationIndex];
|
||||
map.Discover(location, checkTalents: false);
|
||||
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))
|
||||
{
|
||||
IconColor = prefab.IconColor
|
||||
};
|
||||
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;
|
||||
if (destinationIndex != locationIndex && destinationIndex >= 0 && destinationIndex < map.Locations.Count)
|
||||
{
|
||||
Location destination = map.Locations[destinationIndex];
|
||||
connection = map.Connections.FirstOrDefault(c => c.Locations.Contains(location) && c.Locations.Contains(destination));
|
||||
}
|
||||
if (connection != null)
|
||||
{
|
||||
location.UnlockMission(prefab, connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
location.UnlockMission(prefab);
|
||||
LocationConnection? connection = null;
|
||||
if (destinationIndex != locationIndex && destinationIndex >= 0 && destinationIndex < map.Locations.Count)
|
||||
{
|
||||
Location destination = map.Locations[destinationIndex];
|
||||
connection = map.Connections.FirstOrDefault(c => c.Locations.Contains(location) && c.Locations.Contains(destination));
|
||||
}
|
||||
if (connection != null)
|
||||
{
|
||||
location.UnlockMission(prefab, connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
location.UnlockMission(prefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,13 +88,14 @@ namespace Barotrauma
|
||||
public static TextManager.SpeciallyHandledCharCategory ExtractShccFromXElement(XElement element)
|
||||
=> TextManager.SpeciallyHandledCharCategories
|
||||
.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.Japanese => false,
|
||||
|
||||
// For backwards compatibility, we assume that Cyrillic is supported by default
|
||||
TextManager.SpeciallyHandledCharCategory.Cyrillic => true,
|
||||
|
||||
_ => throw new Exception("unreachable")
|
||||
_ => throw new NotImplementedException($"nameof{category} not implemented.")
|
||||
}))
|
||||
.Aggregate(TextManager.SpeciallyHandledCharCategory.None, (current, category) => current | category);
|
||||
|
||||
@@ -517,6 +518,10 @@ namespace Barotrauma
|
||||
GlyphData gd = GetGlyphData(charIndex);
|
||||
if (gd.TexIndex >= 0)
|
||||
{
|
||||
if (gd.TexIndex < 0 || gd.TexIndex >= textures.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"Error while rendering text. Texture index was out of range. Text: {text}, char: {charIndex} index: {gd.TexIndex}, texture count: {textures.Count}");
|
||||
}
|
||||
Texture2D tex = textures[gd.TexIndex];
|
||||
Vector2 drawOffset;
|
||||
drawOffset.X = gd.DrawOffset.X * advanceUnit.X * scale.X - gd.DrawOffset.Y * advanceUnit.Y * scale.Y;
|
||||
|
||||
@@ -533,7 +533,7 @@ namespace Barotrauma
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -532,6 +532,7 @@ namespace Barotrauma
|
||||
void drawRect(Vector2 topLeft, Vector2 bottomRight)
|
||||
{
|
||||
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; }
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
Rect.Location.ToVector2() + topLeft,
|
||||
@@ -801,6 +802,7 @@ namespace Barotrauma
|
||||
IEnumerable<GUITextBox> GetAndSortTextBoxes(GUIComponent parent) => parent.GetAllChildren<GUITextBox>().OrderBy(t => t.Rect.Y).ThenBy(t => t.Rect.X);
|
||||
GUITextBox SelectNextTextBox(GUIListBox listBox)
|
||||
{
|
||||
if (listBox?.SelectedComponent == null) { return null; }
|
||||
var textBoxes = GetAndSortTextBoxes(listBox.SelectedComponent);
|
||||
if (textBoxes.Any())
|
||||
{
|
||||
|
||||
@@ -240,7 +240,7 @@ namespace Barotrauma
|
||||
|
||||
private void UpdatePending()
|
||||
{
|
||||
if (!(pendingHealList is { } healList)) { return; }
|
||||
if (pendingHealList is not { } healList) { return; }
|
||||
|
||||
ImmutableArray<MedicalClinic.NetCrewMember> pendingList = medicalClinic.PendingHeals.ToImmutableArray();
|
||||
|
||||
@@ -493,20 +493,26 @@ namespace Barotrauma
|
||||
|
||||
GUIButton treatAllButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), clinicContainer.RectTransform), TextManager.Get("medicalclinic.treateveryone"))
|
||||
{
|
||||
OnClicked = (_, _) =>
|
||||
OnClicked = (button, _) =>
|
||||
{
|
||||
if (isWaitingForServer) { return true; }
|
||||
|
||||
button.Enabled = false;
|
||||
isWaitingForServer = true;
|
||||
medicalClinic.TreatAllButtonAction(OnReceived);
|
||||
|
||||
bool wasSuccessful = medicalClinic.TreatAllButtonAction(_ => ReEnableButton());
|
||||
if (!wasSuccessful) { ReEnableButton(); }
|
||||
|
||||
void ReEnableButton()
|
||||
{
|
||||
isWaitingForServer = false;
|
||||
button.Enabled = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
crewHealList = new CrewHealList(crewList, parent, treatAllButton);
|
||||
|
||||
void OnReceived(MedicalClinic.CallbackOnlyRequest obj)
|
||||
{
|
||||
isWaitingForServer = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCrewEntry(GUIComponent parent, CrewHealList healList, CharacterInfo info, GUIComponent panel)
|
||||
@@ -585,8 +591,10 @@ namespace Barotrauma
|
||||
OnClicked = (button, _) =>
|
||||
{
|
||||
button.Enabled = false;
|
||||
medicalClinic.HealAllButtonAction(request =>
|
||||
isWaitingForServer = true;
|
||||
bool wasSuccessful = medicalClinic.HealAllButtonAction(request =>
|
||||
{
|
||||
isWaitingForServer = false;
|
||||
switch (request.HealResult)
|
||||
{
|
||||
case MedicalClinic.HealRequestResult.InsufficientFunds:
|
||||
@@ -600,6 +608,12 @@ namespace Barotrauma
|
||||
button.Enabled = true;
|
||||
ClosePopup();
|
||||
});
|
||||
|
||||
if (!wasSuccessful)
|
||||
{
|
||||
isWaitingForServer = false;
|
||||
button.Enabled = true;
|
||||
}
|
||||
ClosePopup();
|
||||
return true;
|
||||
}
|
||||
@@ -610,11 +624,19 @@ namespace Barotrauma
|
||||
ClickSound = GUISoundType.Cart,
|
||||
OnClicked = (button, _) =>
|
||||
{
|
||||
if (isWaitingForServer) { return true; }
|
||||
|
||||
button.Enabled = false;
|
||||
medicalClinic.ClearAllButtonAction(_ =>
|
||||
isWaitingForServer = true;
|
||||
|
||||
bool wasSuccessful = medicalClinic.ClearAllButtonAction(_ => ReEnableButton());
|
||||
if (!wasSuccessful) { ReEnableButton(); }
|
||||
|
||||
void ReEnableButton()
|
||||
{
|
||||
isWaitingForServer = false;
|
||||
button.Enabled = true;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -701,10 +723,15 @@ namespace Barotrauma
|
||||
OnClicked = (button, _) =>
|
||||
{
|
||||
button.Enabled = false;
|
||||
medicalClinic.RemovePendingButtonAction(crewMember, affliction, _ =>
|
||||
bool wasSuccessful = medicalClinic.RemovePendingButtonAction(crewMember, affliction, _ =>
|
||||
{
|
||||
button.Enabled = true;
|
||||
});
|
||||
|
||||
if (!wasSuccessful)
|
||||
{
|
||||
button.Enabled = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -792,7 +819,13 @@ namespace Barotrauma
|
||||
selectedCrewAfflictionList = popupAfflictionList;
|
||||
|
||||
isWaitingForServer = true;
|
||||
medicalClinic.RequestAfflictions(info, OnReceived);
|
||||
bool wasSuccessful = medicalClinic.RequestAfflictions(info, OnReceived);
|
||||
|
||||
if (!wasSuccessful)
|
||||
{
|
||||
isWaitingForServer = false;
|
||||
ClosePopup();
|
||||
}
|
||||
|
||||
void OnReceived(MedicalClinic.AfflictionRequest request)
|
||||
{
|
||||
@@ -800,6 +833,16 @@ namespace Barotrauma
|
||||
|
||||
if (request.Result != MedicalClinic.RequestResult.Success)
|
||||
{
|
||||
switch (request.Result)
|
||||
{
|
||||
case MedicalClinic.RequestResult.CharacterInfoMissing:
|
||||
DebugConsole.ThrowError($"Unable to select character \"{info.Character?.DisplayName}\" in medical clini because the character health was missing.");
|
||||
break;
|
||||
case MedicalClinic.RequestResult.CharacterNotFound:
|
||||
DebugConsole.ThrowError($"Unable to select character \"{info.Character?.DisplayName} in medical clinic because the server was unable to find a character with ID {info.ID}.");
|
||||
break;
|
||||
}
|
||||
|
||||
feedbackBlock.Text = GetErrorText(request.Result);
|
||||
feedbackBlock.TextColor = GUIStyle.Red;
|
||||
return;
|
||||
@@ -953,14 +996,20 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
existingMember.Afflictions = existingMember.Afflictions.Concat(afflictions).ToImmutableArray();
|
||||
|
||||
ToggleElements(ElementState.Disabled, elementsToDisable);
|
||||
medicalClinic.AddPendingButtonAction(existingMember, request =>
|
||||
bool wasSuccessful = medicalClinic.AddPendingButtonAction(existingMember, request =>
|
||||
{
|
||||
if (request.Result == MedicalClinic.RequestResult.Timeout)
|
||||
{
|
||||
ToggleElements(ElementState.Enabled, elementsToDisable);
|
||||
}
|
||||
});
|
||||
|
||||
if (!wasSuccessful)
|
||||
{
|
||||
ToggleElements(ElementState.Enabled, elementsToDisable);
|
||||
}
|
||||
}
|
||||
|
||||
#warning TODO: this doesn't seem like the right place for this, and it's not clear from the method signature how this differs from ToolBox.LimitString
|
||||
@@ -1090,9 +1139,8 @@ namespace Barotrauma
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
MedicalClinic.RequestResult.Error => TextManager.Get("error"),
|
||||
MedicalClinic.RequestResult.Timeout => TextManager.Get("medicalclinic.requesttimeout"),
|
||||
_ => "What the hell did you just do" // this should never happen
|
||||
_ => TextManager.Get("error")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -175,6 +175,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (relativeOffset.NearlyEquals(value)) { return; }
|
||||
relativeOffset = value;
|
||||
recalculateRect = true;
|
||||
RecalculateChildren(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,7 +870,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var minRep in priceInfo.MinReputation)
|
||||
{
|
||||
if (campaign.GetReputation(minRep.Key) < minRep.Value)
|
||||
if (MathF.Round(campaign.GetReputation(minRep.Key)) < minRep.Value)
|
||||
{
|
||||
return minRep;
|
||||
}
|
||||
@@ -1930,7 +1930,7 @@ namespace Barotrauma
|
||||
"campaignstore.reputationrequired",
|
||||
("[amount]", ((int)requiredReputation.Value.Value).ToString()),
|
||||
("[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;
|
||||
toolTip += $"\n‖color:{color.ToStringHex()}‖{repStr}‖color:end‖";
|
||||
}
|
||||
|
||||
@@ -807,8 +807,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
|
||||
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch);
|
||||
if (GameMain.GameSession.TryPurchaseSubmarine(selectedSubmarine))
|
||||
{
|
||||
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch);
|
||||
}
|
||||
RefreshSubmarineDisplay(true);
|
||||
}
|
||||
else
|
||||
@@ -829,7 +831,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
|
||||
GameMain.GameSession.TryPurchaseSubmarine(selectedSubmarine);
|
||||
RefreshSubmarineDisplay(true);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -23,9 +23,12 @@ namespace Barotrauma
|
||||
private float votingTime = 100f;
|
||||
private float timer;
|
||||
private VoteType currentVoteType;
|
||||
private Color SubmarineColor => GUIStyle.Orange;
|
||||
private static Color SubmarineColor => GUIStyle.Orange;
|
||||
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)
|
||||
{
|
||||
if (starter == null || info == null) { return null; }
|
||||
|
||||
@@ -664,7 +664,10 @@ namespace Barotrauma
|
||||
while (Timing.Accumulator >= Timing.Step)
|
||||
{
|
||||
Timing.TotalTime += Timing.Step;
|
||||
|
||||
if (!Paused)
|
||||
{
|
||||
Timing.TotalTimeUnpaused += Timing.Step;
|
||||
}
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
@@ -944,7 +947,10 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Barotrauma
|
||||
// Item must be in a non-equipment slot if possible
|
||||
if (!item.AllowedSlots.All(s => equipmentSlots.Contains(s)) && IsInEquipmentSlot(item)) { return false; }
|
||||
// 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;
|
||||
}, recursive: true).Distinct();
|
||||
|
||||
|
||||
@@ -166,6 +166,9 @@ namespace Barotrauma
|
||||
if (Submarine.MainSub == null || Level.Loaded == null) { return; }
|
||||
|
||||
bool allowEndingRound = false;
|
||||
endRoundButton.Color = endRoundButton.Style.Color;
|
||||
endRoundButton.HoverColor = endRoundButton.Style.HoverColor;
|
||||
RichString overrideEndRoundButtonToolTip = string.Empty;
|
||||
var availableTransition = GetAvailableTransition(out _, out Submarine leavingSub);
|
||||
LocalizedString buttonText = "";
|
||||
switch (availableTransition)
|
||||
@@ -194,13 +197,23 @@ namespace Barotrauma
|
||||
break;
|
||||
case TransitionType.None:
|
||||
default:
|
||||
if (Level.Loaded.Type == LevelData.LevelType.Outpost &&
|
||||
!Level.Loaded.IsEndBiome &&
|
||||
(Character.Controlled?.Submarine?.Info.Type == SubmarineType.Player || (Character.Controlled?.CurrentHull?.OutpostModuleTags.Contains("airlock".ToIdentifier()) ?? false)))
|
||||
bool inFriendlySub = Character.Controlled is { IsInFriendlySub: true };
|
||||
if (Level.Loaded.Type == LevelData.LevelType.Outpost && !Level.Loaded.IsEndBiome &&
|
||||
(inFriendlySub || (Character.Controlled?.CurrentHull?.OutpostModuleTags.Contains("airlock".ToIdentifier()) ?? false)))
|
||||
{
|
||||
if (Missions.Any(m => m is SalvageMission salvageMission && salvageMission.AnyTargetNeedsToBeRetrievedToSub))
|
||||
{
|
||||
overrideEndRoundButtonToolTip = TextManager.Get("SalvageTargetNotInSub");
|
||||
endRoundButton.Color = GUIStyle.Red * 0.7f;
|
||||
endRoundButton.HoverColor = GUIStyle.Red;
|
||||
}
|
||||
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
|
||||
allowEndingRound = !ForceMapUI && !ShowCampaignUI;
|
||||
}
|
||||
else
|
||||
{
|
||||
allowEndingRound = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (Level.IsLoadedOutpost && !ObjectiveManager.AllActiveObjectivesCompleted())
|
||||
@@ -227,7 +240,11 @@ namespace Barotrauma
|
||||
prevCampaignUIAutoOpenType = availableTransition;
|
||||
}
|
||||
endRoundButton.Text = ToolBox.LimitString(buttonText.Value, endRoundButton.Font, endRoundButton.Rect.Width - 5);
|
||||
if (endRoundButton.Text != buttonText)
|
||||
if (overrideEndRoundButtonToolTip != string.Empty)
|
||||
{
|
||||
endRoundButton.ToolTip = overrideEndRoundButtonToolTip;
|
||||
}
|
||||
else if (endRoundButton.Text != buttonText)
|
||||
{
|
||||
endRoundButton.ToolTip = buttonText;
|
||||
}
|
||||
|
||||
+7
-3
@@ -193,7 +193,7 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
yield return CoroutineStatus.Failure;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
if (GameMain.Client.LateCampaignJoin)
|
||||
@@ -335,7 +335,7 @@ namespace Barotrauma
|
||||
//--------------------------------------
|
||||
|
||||
//wait for the new level to be loaded
|
||||
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, seconds: 60);
|
||||
DateTime timeOut = DateTime.Now + GameClient.LevelTransitionTimeOut;
|
||||
while (Level.Loaded == prevLevel || Level.Loaded == null)
|
||||
{
|
||||
if (DateTime.Now > timeOut || Screen.Selected != GameMain.GameScreen) { break; }
|
||||
@@ -345,7 +345,11 @@ namespace Barotrauma
|
||||
endTransition.Stop();
|
||||
overlayColor = Color.Transparent;
|
||||
|
||||
if (DateTime.Now > timeOut) { GameMain.NetLobbyScreen.Select(); }
|
||||
if (DateTime.Now > timeOut)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to start the round. Timed out while waiting for the level transition to finish.");
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
}
|
||||
if (Screen.Selected is not RoundSummaryScreen)
|
||||
{
|
||||
if (continueButton != null)
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Barotrauma
|
||||
{
|
||||
Undecided,
|
||||
Success,
|
||||
Error,
|
||||
CharacterInfoMissing,
|
||||
CharacterNotFound,
|
||||
Timeout
|
||||
}
|
||||
|
||||
@@ -34,7 +35,9 @@ namespace Barotrauma
|
||||
private readonly List<RequestAction<CallbackOnlyRequest>> addRequests = new List<RequestAction<CallbackOnlyRequest>>();
|
||||
private readonly List<RequestAction<CallbackOnlyRequest>> removeRequests = new List<RequestAction<CallbackOnlyRequest>>();
|
||||
|
||||
public void RequestAfflictions(CharacterInfo info, Action<AfflictionRequest> onReceived)
|
||||
private static readonly LeakyBucket requestBucket = new(RateLimitExpiry / (float)RateLimitMaxRequests, 10);
|
||||
|
||||
public bool RequestAfflictions(CharacterInfo info, Action<AfflictionRequest> onReceived)
|
||||
{
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
@@ -42,23 +45,26 @@ namespace Barotrauma
|
||||
if (Screen.Selected is TestScreen)
|
||||
{
|
||||
onReceived.Invoke(new AfflictionRequest(RequestResult.Success, TestAfflictions.ToImmutableArray()));
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (info is not { Character.CharacterHealth: { } health })
|
||||
{
|
||||
onReceived.Invoke(new AfflictionRequest(RequestResult.Error, ImmutableArray<NetAffliction>.Empty));
|
||||
return;
|
||||
onReceived.Invoke(new AfflictionRequest(RequestResult.CharacterInfoMissing, ImmutableArray<NetAffliction>.Empty));
|
||||
return true;
|
||||
}
|
||||
|
||||
ImmutableArray<NetAffliction> pendingAfflictions = GetAllAfflictions(health).ToImmutableArray();
|
||||
ImmutableArray<NetAffliction> pendingAfflictions = GetAllAfflictions(health);
|
||||
onReceived.Invoke(new AfflictionRequest(RequestResult.Success, pendingAfflictions));
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
afflictionRequests.Add(new RequestAction<AfflictionRequest>(onReceived, GetTimeout()));
|
||||
SendAfflictionRequest(info);
|
||||
return requestBucket.TryEnqueue(() =>
|
||||
{
|
||||
afflictionRequests.Add(new RequestAction<AfflictionRequest>(onReceived, GetTimeout()));
|
||||
SendAfflictionRequest(info);
|
||||
});
|
||||
}
|
||||
|
||||
public void RequestLatestPending(Action<PendingRequest> onReceived)
|
||||
@@ -66,8 +72,11 @@ namespace Barotrauma
|
||||
// no need to worry about syncing when there's only one pair of eyes capable of looking at the UI
|
||||
if (GameMain.IsSingleplayer) { return; }
|
||||
|
||||
pendingHealRequests.Add(new RequestAction<PendingRequest>(onReceived, GetTimeout()));
|
||||
SendPendingRequest();
|
||||
requestBucket.TryEnqueue(() =>
|
||||
{
|
||||
pendingHealRequests.Add(new RequestAction<PendingRequest>(onReceived, GetTimeout()));
|
||||
SendPendingRequest();
|
||||
});
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
@@ -79,6 +88,7 @@ namespace Barotrauma
|
||||
UpdateQueue(clearAllRequests, now, onTimeout: CallbackOnlyTimeout);
|
||||
UpdateQueue(addRequests, now, onTimeout: CallbackOnlyTimeout);
|
||||
UpdateQueue(removeRequests, now, onTimeout: CallbackOnlyTimeout);
|
||||
requestBucket.Update(deltaTime);
|
||||
|
||||
static void CallbackOnlyTimeout(Action<CallbackOnlyRequest> callback) { callback(new CallbackOnlyRequest(RequestResult.Timeout)); }
|
||||
}
|
||||
@@ -146,21 +156,25 @@ namespace Barotrauma
|
||||
return (from client in clients where client.Name == ownName select client.Ping).FirstOrDefault();
|
||||
}
|
||||
|
||||
public void TreatAllButtonAction(Action<CallbackOnlyRequest> onReceived)
|
||||
public bool TreatAllButtonAction(Action<CallbackOnlyRequest> onReceived)
|
||||
{
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
AddEverythingToPending();
|
||||
onReceived(new CallbackOnlyRequest(RequestResult.Success));
|
||||
OnUpdate?.Invoke();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
addRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(null, NetworkHeader.ADD_EVERYTHING_TO_PENDING, DeliveryMethod.Reliable);
|
||||
return requestBucket.TryEnqueue(() =>
|
||||
{
|
||||
addRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(null, NetworkHeader.ADD_EVERYTHING_TO_PENDING, DeliveryMethod.Reliable);
|
||||
});
|
||||
}
|
||||
|
||||
public void HealAllButtonAction(Action<HealRequest> onReceived)
|
||||
|
||||
public bool HealAllButtonAction(Action<HealRequest> onReceived)
|
||||
{
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
@@ -171,33 +185,39 @@ namespace Barotrauma
|
||||
OnUpdate?.Invoke();
|
||||
}
|
||||
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (campaign?.CampaignUI?.MedicalClinic is { } ui)
|
||||
if (campaign?.CampaignUI?.MedicalClinic is { } openedUi)
|
||||
{
|
||||
ui.ClosePopup();
|
||||
openedUi.ClosePopup();
|
||||
}
|
||||
|
||||
healAllRequests.Add(new RequestAction<HealRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(null, NetworkHeader.HEAL_PENDING, DeliveryMethod.Reliable);
|
||||
return requestBucket.TryEnqueue(() =>
|
||||
{
|
||||
healAllRequests.Add(new RequestAction<HealRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(null, NetworkHeader.HEAL_PENDING, DeliveryMethod.Reliable);
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearAllButtonAction(Action<CallbackOnlyRequest> onReceived)
|
||||
public bool ClearAllButtonAction(Action<CallbackOnlyRequest> onReceived)
|
||||
{
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
ClearPendingHeals();
|
||||
onReceived(new CallbackOnlyRequest(RequestResult.Success));
|
||||
OnUpdate?.Invoke();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
clearAllRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(null, NetworkHeader.CLEAR_PENDING, DeliveryMethod.Reliable);
|
||||
return requestBucket.TryEnqueue(() =>
|
||||
{
|
||||
clearAllRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(null, NetworkHeader.CLEAR_PENDING, DeliveryMethod.Reliable);
|
||||
});
|
||||
}
|
||||
|
||||
private void ClearRequstReceived()
|
||||
private void ClearRequestReceived()
|
||||
{
|
||||
ClearPendingHeals();
|
||||
if (TryDequeue(clearAllRequests, out var callback))
|
||||
@@ -224,28 +244,31 @@ namespace Barotrauma
|
||||
OnUpdate?.Invoke();
|
||||
}
|
||||
|
||||
public void AddPendingButtonAction(NetCrewMember crewMember, Action<CallbackOnlyRequest> onReceived)
|
||||
public bool AddPendingButtonAction(NetCrewMember crewMember, Action<CallbackOnlyRequest> onReceived)
|
||||
{
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
InsertPendingCrewMember(crewMember);
|
||||
onReceived(new CallbackOnlyRequest(RequestResult.Success));
|
||||
OnUpdate?.Invoke();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
addRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(crewMember, NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable);
|
||||
return requestBucket.TryEnqueue(() =>
|
||||
{
|
||||
addRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(crewMember, NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable);
|
||||
});
|
||||
}
|
||||
|
||||
public void RemovePendingButtonAction(NetCrewMember crewMember, NetAffliction affliction, Action<CallbackOnlyRequest> onReceived)
|
||||
public bool RemovePendingButtonAction(NetCrewMember crewMember, NetAffliction affliction, Action<CallbackOnlyRequest> onReceived)
|
||||
{
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
RemovePendingAffliction(crewMember, affliction);
|
||||
onReceived(new CallbackOnlyRequest(RequestResult.Success));
|
||||
OnUpdate?.Invoke();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
INetSerializableStruct removedAffliction = new NetRemovedAffliction
|
||||
@@ -254,11 +277,14 @@ namespace Barotrauma
|
||||
Affliction = affliction
|
||||
};
|
||||
|
||||
removeRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(removedAffliction, NetworkHeader.REMOVE_PENDING, DeliveryMethod.Reliable);
|
||||
return requestBucket.TryEnqueue(() =>
|
||||
{
|
||||
removeRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(removedAffliction, NetworkHeader.REMOVE_PENDING, DeliveryMethod.Reliable);
|
||||
});
|
||||
}
|
||||
|
||||
private void NewAdditonReceived(IReadMessage inc, MessageFlag flag)
|
||||
private void NewAdditionReceived(IReadMessage inc, MessageFlag flag)
|
||||
{
|
||||
var crewMembers = INetSerializableStruct.Read<NetCollection<NetCrewMember>>(inc);
|
||||
foreach (var crewMember in crewMembers)
|
||||
@@ -300,7 +326,7 @@ namespace Barotrauma
|
||||
NetCrewMember crewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
|
||||
if (TryDequeue(afflictionRequests, out var callback))
|
||||
{
|
||||
RequestResult result = crewMember.CharacterInfoID is 0 ? RequestResult.Error : RequestResult.Success;
|
||||
RequestResult result = crewMember.CharacterInfoID is 0 ? RequestResult.CharacterNotFound : RequestResult.Success;
|
||||
callback(new AfflictionRequest(result, crewMember.Afflictions.ToImmutableArray()));
|
||||
}
|
||||
}
|
||||
@@ -336,7 +362,7 @@ namespace Barotrauma
|
||||
IWriteMessage msg = StartSending();
|
||||
msg.WriteByte((byte)header);
|
||||
netStruct?.Write(msg);
|
||||
GameMain.Client.ClientPeer?.Send(msg, deliveryMethod);
|
||||
GameMain.Client?.ClientPeer?.Send(msg, deliveryMethod);
|
||||
}
|
||||
|
||||
public void ClientRead(IReadMessage inc)
|
||||
@@ -356,7 +382,7 @@ namespace Barotrauma
|
||||
PendingRequestReceived(inc);
|
||||
break;
|
||||
case NetworkHeader.ADD_PENDING:
|
||||
NewAdditonReceived(inc, flag);
|
||||
NewAdditionReceived(inc, flag);
|
||||
break;
|
||||
case NetworkHeader.REMOVE_PENDING:
|
||||
NewRemovalReceived(inc, flag);
|
||||
@@ -365,7 +391,7 @@ namespace Barotrauma
|
||||
HealRequestReceived(inc);
|
||||
break;
|
||||
case NetworkHeader.CLEAR_PENDING:
|
||||
ClearRequstReceived();
|
||||
ClearRequestReceived();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,9 +310,10 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
RichString.Rich(missionMessage), wrap: true);
|
||||
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed)
|
||||
if (selectedMissions.Contains(displayedMission))
|
||||
{
|
||||
RichString reputationText = displayedMission.GetReputationRewardText();
|
||||
if (!reputationText.IsNullOrEmpty())
|
||||
@@ -324,7 +325,7 @@ namespace Barotrauma
|
||||
if (totalReward > 0)
|
||||
{
|
||||
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));
|
||||
if (share > 0)
|
||||
|
||||
@@ -575,7 +575,7 @@ namespace Barotrauma
|
||||
//cancel dragging if too far away from the container of the dragged item
|
||||
if (DraggingItems.Any())
|
||||
{
|
||||
var rootContainer = DraggingItems.First().GetRootContainer();
|
||||
var rootContainer = DraggingItems.First().RootContainer;
|
||||
var rootInventory = DraggingItems.First().ParentInventory;
|
||||
|
||||
if (rootContainer != null)
|
||||
|
||||
@@ -155,8 +155,6 @@ namespace Barotrauma.Items.Components
|
||||
convexHull.Enabled = true;
|
||||
SetVertices(convexHull, rect);
|
||||
}
|
||||
convexHull.IsExteriorWall = !linkedGap.IsRoomToRoom;
|
||||
if (convexHull2 != null) { convexHull2.IsExteriorWall = convexHull.IsExteriorWall; }
|
||||
}
|
||||
|
||||
|
||||
@@ -169,12 +167,11 @@ namespace Barotrauma.Items.Components
|
||||
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)
|
||||
{
|
||||
convexHull.IsExteriorWall = !linkedGap.IsRoomToRoom;
|
||||
if (convexHull2 != null) { convexHull2.IsExteriorWall = convexHull.IsExteriorWall; }
|
||||
if (shakeTimer > 0.0f)
|
||||
{
|
||||
shakeTimer -= deltaTime;
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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
|
||||
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
|
||||
needsScrolling = false;
|
||||
scrollingText = DisplayText.Value;
|
||||
TextBlock.Text = scrollingText = DisplayText.Value;
|
||||
scrollPadding = 0;
|
||||
scrollAmount = 0.0f;
|
||||
scrollIndex = 0;
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace Barotrauma.Items.Components
|
||||
Step = 0.05f,
|
||||
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
lastReceivedTargetForce = null;
|
||||
float newTargetForce = barScroll * 200.0f - 100.0f;
|
||||
if (Math.Abs(newTargetForce - targetForce) < 0.01) { return false; }
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Barotrauma.Items.Components
|
||||
private GUIFrame selectedItemFrame;
|
||||
private GUIFrame selectedItemReqsFrame;
|
||||
|
||||
private GUITextBlock amountTextMin, amountTextMax;
|
||||
private GUITextBlock amountTextMax;
|
||||
private GUIScrollBar amountInput;
|
||||
|
||||
public GUIButton ActivateButton
|
||||
@@ -29,6 +29,9 @@ namespace Barotrauma.Items.Components
|
||||
private GUIComponent outputSlot;
|
||||
private GUIComponent inputInventoryHolder, outputInventoryHolder;
|
||||
|
||||
private readonly List<GUIButton> itemCategoryButtons = new List<GUIButton>();
|
||||
private MapEntityCategory? selectedItemCategory;
|
||||
|
||||
public FabricationRecipe SelectedItem
|
||||
{
|
||||
get { return selectedItem; }
|
||||
@@ -77,7 +80,67 @@ namespace Barotrauma.Items.Components
|
||||
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,
|
||||
Stretch = true,
|
||||
@@ -105,10 +168,13 @@ namespace Barotrauma.Items.Components
|
||||
Padding = Vector4.Zero,
|
||||
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) =>
|
||||
{
|
||||
FilterEntities(text);
|
||||
FilterEntities(selectedItemCategory, text);
|
||||
return true;
|
||||
};
|
||||
filterArea.RectTransform.MaxSize = new Point(int.MaxValue, itemFilterBox.Rect.Height);
|
||||
@@ -174,7 +240,7 @@ namespace Barotrauma.Items.Components
|
||||
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")
|
||||
{
|
||||
@@ -489,15 +555,37 @@ namespace Barotrauma.Items.Components
|
||||
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;
|
||||
itemIcon.Draw(
|
||||
spriteBatch,
|
||||
slotRect.Center.ToVector2(),
|
||||
color: requiredItemPrefab.InventoryIconColor * 0.3f,
|
||||
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y));
|
||||
|
||||
|
||||
var requiredItemPrefab = requiredItem.FirstMatchingPrefab;
|
||||
|
||||
float iconAlpha = 0.0f;
|
||||
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)
|
||||
{
|
||||
Vector2 stackCountPos = new Vector2(slotRect.Right, slotRect.Bottom);
|
||||
@@ -552,9 +640,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
toolTipText = $"‖color:{Color.White.ToStringHex()}‖{toolTipText}‖color:end‖";
|
||||
if (!requiredItemPrefab.Description.IsNullOrEmpty())
|
||||
if (!requiredItem.OverrideDescription.IsNullOrEmpty())
|
||||
{
|
||||
toolTipText = '\n' + requiredItemPrefab.Description;
|
||||
toolTipText += '\n' + requiredItem.OverrideDescription;
|
||||
}
|
||||
else if (!requiredItemPrefab.Description.IsNullOrEmpty())
|
||||
{
|
||||
toolTipText += '\n' + requiredItemPrefab.Description;
|
||||
}
|
||||
tooltip = new ToolTip { TargetElement = slotRect, Tooltip = toolTipText };
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (GUIComponent child in itemList.Content.Children)
|
||||
{
|
||||
FabricationRecipe recipe = child.UserData as FabricationRecipe;
|
||||
if (recipe?.DisplayName == null) { continue; }
|
||||
child.Visible = recipe.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
FabricationRecipe recipe = child.UserData as FabricationRecipe;
|
||||
if (recipe?.DisplayName == null) { continue; }
|
||||
child.Visible =
|
||||
(string.IsNullOrWhiteSpace(filter) || recipe.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase)) &&
|
||||
(!category.HasValue || recipe.TargetItem.Category.HasFlag(category.Value));
|
||||
}
|
||||
|
||||
foreach (GUIButton btn in itemCategoryButtons)
|
||||
{
|
||||
btn.Selected = (MapEntityCategory?)btn.UserData == selectedItemCategory;
|
||||
}
|
||||
HideEmptyItemListCategories();
|
||||
|
||||
return true;
|
||||
@@ -648,7 +739,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public bool ClearFilter()
|
||||
{
|
||||
FilterEntities("");
|
||||
FilterEntities(selectedItemCategory, "");
|
||||
itemList.UpdateScrollBarSize();
|
||||
itemList.BarScroll = 0.0f;
|
||||
itemFilterBox.Text = "";
|
||||
@@ -737,6 +828,7 @@ namespace Barotrauma.Items.Components
|
||||
TextManager.Get("FabricatorRequiredSkills"), textColor: inadequateSkills.Any() ? GUIStyle.Red : GUIStyle.Green, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
AutoScaleHorizontal = true,
|
||||
ToolTip = TextManager.Get("fabricatorrequiredskills.tooltip")
|
||||
};
|
||||
foreach (Skill skill in selectedItem.RequiredSkills)
|
||||
{
|
||||
|
||||
@@ -125,18 +125,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
public static MiniMapSettings Default = new MiniMapSettings
|
||||
(
|
||||
ignoreOutposts: false,
|
||||
createHullElements: true,
|
||||
elementColor: MiniMap.MiniMapBaseColor
|
||||
);
|
||||
|
||||
public readonly bool IgnoreOutposts;
|
||||
public readonly bool CreateHullElements;
|
||||
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;
|
||||
ElementColor = elementColor ?? MiniMap.MiniMapBaseColor;
|
||||
}
|
||||
@@ -403,7 +400,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool VisibleOnItemFinder(Item it)
|
||||
{
|
||||
if (!item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true)) { return false; }
|
||||
if (it?.Submarine == null) { return false; }
|
||||
if (item.Submarine == null || !item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true)) { return false; }
|
||||
if (it.NonInteractable || it.HiddenInGame) { return false; }
|
||||
if (it.GetComponent<Pickable>() == null) { return false; }
|
||||
|
||||
@@ -436,7 +434,11 @@ namespace Barotrauma.Items.Components
|
||||
prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
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));
|
||||
miniMapContainer = new GUIFrame(new RectTransform(Vector2.One, scissorComponent.Content.RectTransform, Anchor.Center), style: null) { CanBeFocused = false };
|
||||
@@ -444,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();
|
||||
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);
|
||||
electricalFrame = CreateMiniMap(item.Submarine, miniMapContainer, new MiniMapSettings(createHullElements: false), electrialPointsOfInterest, out electricalMapComponents);
|
||||
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), electricalPointsOfInterest, out electricalMapComponents);
|
||||
|
||||
Dictionary<MiniMapGUIComponent, GUIComponent> electricChildren = new Dictionary<MiniMapGUIComponent, GUIComponent>();
|
||||
|
||||
@@ -535,7 +537,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
displayedSubs.Clear();
|
||||
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();
|
||||
|
||||
@@ -550,7 +552,7 @@ namespace Barotrauma.Items.Components
|
||||
item.Submarine is { } itemSub &&
|
||||
(
|
||||
!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
|
||||
) ||
|
||||
prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight || // resolution changed
|
||||
@@ -730,7 +732,7 @@ namespace Barotrauma.Items.Components
|
||||
if (sprite != null && ShowHullIntegrity)
|
||||
{
|
||||
Vector2 spriteSize = sprite.size;
|
||||
Rectangle worldBorders = item.Submarine.GetDockedBorders();
|
||||
Rectangle worldBorders = item.Submarine.GetDockedBorders(allowDifferentTeam: false);
|
||||
worldBorders.Location += item.Submarine.WorldPosition.ToPoint();
|
||||
foreach (Gap gap in Gap.GapList)
|
||||
{
|
||||
@@ -914,7 +916,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
|
||||
RectangleF dockedBorders = item.Submarine.GetDockedBorders();
|
||||
RectangleF dockedBorders = item.Submarine.GetDockedBorders(allowDifferentTeam: false);
|
||||
dockedBorders.Location += item.Submarine.WorldPosition;
|
||||
RectangleF parentRect = miniMapFrame.Rect;
|
||||
|
||||
@@ -1063,7 +1065,9 @@ namespace Barotrauma.Items.Components
|
||||
waterVolume += linkedHull.WaterVolume;
|
||||
totalVolume += linkedHull.Volume;
|
||||
}
|
||||
hullData.HullWaterAmount = MathHelper.Clamp((int)Math.Ceiling(waterVolume / totalVolume * 100), 0, 100);
|
||||
hullData.HullWaterAmount =
|
||||
waterVolume > 1.0f ?
|
||||
MathHelper.Clamp((int)Math.Ceiling(waterVolume / totalVolume * 100), 0, 100) : 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1302,7 +1306,7 @@ namespace Barotrauma.Items.Components
|
||||
GameMain.Instance.GraphicsDevice.SetRenderTarget(rt);
|
||||
GameMain.Instance.GraphicsDevice.Clear(Color.Transparent);
|
||||
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();
|
||||
|
||||
parentRect.Inflate(-inflate, -inflate);
|
||||
@@ -1523,7 +1527,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Dictionary<MapEntity, MiniMapGUIComponent> pointsOfInterestCollection = new Dictionary<MapEntity, MiniMapGUIComponent>();
|
||||
|
||||
RectangleF worldBorders = sub.GetDockedBorders();
|
||||
RectangleF worldBorders = sub.GetDockedBorders(allowDifferentTeam: false);
|
||||
worldBorders.Location += sub.WorldPosition;
|
||||
|
||||
// create a container that has the same "aspect ratio" as the sub
|
||||
@@ -1536,7 +1540,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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;
|
||||
ImmutableDictionary<Hull, ImmutableArray<Hull>> combinedHulls = ImmutableDictionary<Hull, ImmutableArray<Hull>>.Empty;
|
||||
|
||||
@@ -1683,7 +1687,7 @@ namespace Barotrauma.Items.Components
|
||||
bool IsPartofSub(MapEntity entity)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Barotrauma.Items.Components
|
||||
private const float NearbyObjectUpdateInterval = 1.0f;
|
||||
float nearbyObjectUpdateTimer;
|
||||
|
||||
private List<Submarine> connectedSubs = new List<Submarine>();
|
||||
private readonly List<Submarine> connectedSubs = new List<Submarine>();
|
||||
private const float ConnectedSubUpdateInterval = 1.0f;
|
||||
float connectedSubUpdateTimer;
|
||||
|
||||
@@ -335,9 +335,11 @@ namespace Barotrauma.Items.Components
|
||||
// Setup layout for nav terminal
|
||||
if (isConnectedToSteering || RightLayout)
|
||||
{
|
||||
controlContainer.RectTransform.AbsoluteOffset = Point.Zero;
|
||||
controlContainer.RectTransform.RelativeOffset = controlBoxOffset;
|
||||
controlContainer.RectTransform.SetPosition(Anchor.TopRight);
|
||||
sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest;
|
||||
if (HasMineralScanner) { PreventMineralScannerOverlap(); }
|
||||
sonarView.RectTransform.SetPosition(Anchor.CenterLeft);
|
||||
sonarView.RectTransform.Resize(GUISizeCalculation);
|
||||
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);
|
||||
mineralScannerSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), mineralScannerFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal")
|
||||
{
|
||||
Selected = UseMineralScanner,
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
useMineralScanner = !useMineralScanner;
|
||||
button.Selected = useMineralScanner;
|
||||
UseMineralScanner = !UseMineralScanner;
|
||||
button.Selected = UseMineralScanner;
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
unsentChanges = true;
|
||||
@@ -496,12 +499,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (transducer.Transducer.Item.Submarine == null) { 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)
|
||||
{
|
||||
connectedSubs = item.Submarine?.GetConnectedSubs();
|
||||
connectedSubs.AddRange(item.Submarine?.GetConnectedSubs());
|
||||
}
|
||||
connectedSubUpdateTimer = ConnectedSubUpdateInterval;
|
||||
}
|
||||
@@ -1032,7 +1035,7 @@ namespace Barotrauma.Items.Components
|
||||
missionIndex++;
|
||||
}
|
||||
|
||||
if (HasMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null &&
|
||||
if (HasMineralScanner && UseMineralScanner && CurrentMode == Mode.Active && MineralClusters != null &&
|
||||
(item.CurrentHull == null || !DetectSubmarineWalls))
|
||||
{
|
||||
foreach (var c in MineralClusters)
|
||||
@@ -1311,7 +1314,6 @@ namespace Barotrauma.Items.Components
|
||||
float worldPingRadiusSqr = worldPingRadius * worldPingRadius;
|
||||
|
||||
disruptedDirections.Clear();
|
||||
if (Level.Loaded == null) { return; }
|
||||
|
||||
for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
|
||||
{
|
||||
@@ -1513,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();
|
||||
if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr)
|
||||
@@ -1923,7 +1926,7 @@ namespace Barotrauma.Items.Components
|
||||
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
|
||||
msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
|
||||
}
|
||||
msg.WriteBoolean(useMineralScanner);
|
||||
msg.WriteBoolean(UseMineralScanner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1935,7 +1938,7 @@ namespace Barotrauma.Items.Components
|
||||
float zoomT = 1.0f;
|
||||
bool directionalPing = useDirectionalPing;
|
||||
float directionT = 0.0f;
|
||||
bool mineralScanner = useMineralScanner;
|
||||
bool mineralScanner = UseMineralScanner;
|
||||
if (isActive)
|
||||
{
|
||||
zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
@@ -1966,7 +1969,7 @@ namespace Barotrauma.Items.Components
|
||||
pingDirection = new Vector2((float)Math.Cos(pingAngle), (float)Math.Sin(pingAngle));
|
||||
}
|
||||
useDirectionalPing = directionalModeSwitch.Selected = directionalPing;
|
||||
useMineralScanner = mineralScanner;
|
||||
UseMineralScanner = mineralScanner;
|
||||
if (mineralScannerSwitch != null)
|
||||
{
|
||||
mineralScannerSwitch.Selected = mineralScanner;
|
||||
@@ -1983,7 +1986,7 @@ namespace Barotrauma.Items.Components
|
||||
directionalModeSwitch.Selected = useDirectionalPing;
|
||||
if (mineralScannerSwitch != null)
|
||||
{
|
||||
mineralScannerSwitch.Selected = useMineralScanner;
|
||||
mineralScannerSwitch.Selected = UseMineralScanner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma.Items.Components
|
||||
User = Entity.FindEntityByID(userId) as Character;
|
||||
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
float rotation = msg.ReadSingle();
|
||||
SpreadCounter = msg.ReadByte();
|
||||
spreadIndex = msg.ReadByte();
|
||||
if (User != null)
|
||||
{
|
||||
Shoot(User, simPosition, simPosition, rotation, ignoredBodies: User.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList(), createNetworkEvent: false);
|
||||
|
||||
@@ -21,7 +21,8 @@ namespace Barotrauma.Items.Components
|
||||
public float FlashTimer { 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)
|
||||
{
|
||||
@@ -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
|
||||
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
|
||||
if (c.IsOutput)
|
||||
{
|
||||
@@ -127,7 +166,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (DraggingConnected != null)
|
||||
{
|
||||
if (mouseInRect)
|
||||
@@ -225,7 +263,9 @@ namespace Barotrauma.Items.Components
|
||||
GUI.DrawString(spriteBatch, labelPos, text, GUIStyle.TextColorBright, font: GUIStyle.SmallFont);
|
||||
|
||||
float connectorSpriteScale = (35.0f / connectionSprite.SourceRect.Width) * panel.Scale;
|
||||
|
||||
connectionSprite.Draw(spriteBatch, position, scale: connectorSpriteScale);
|
||||
|
||||
}
|
||||
|
||||
private void DrawWires(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 wirePosition, bool mouseIn, Wire equippedWire, float wireInterval)
|
||||
@@ -307,6 +347,63 @@ namespace Barotrauma.Items.Components
|
||||
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)
|
||||
{
|
||||
int textX = (int)start.X;
|
||||
|
||||
@@ -10,6 +10,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
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
|
||||
const float RewireSoundDuration = 5.0f;
|
||||
|
||||
@@ -120,12 +124,15 @@ namespace Barotrauma.Items.Components
|
||||
if (user != Character.Controlled || user == null) { return; }
|
||||
|
||||
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])
|
||||
{
|
||||
sprite.Draw(spriteBatch, GuiFrame.Rect, Color.White, SpriteEffects.None);
|
||||
}
|
||||
if (!tooltip.text.IsNullOrEmpty())
|
||||
{
|
||||
GUIComponent.DrawToolTip(spriteBatch, tooltip.text, tooltip.tooltipPos);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckForLabelOverlap()
|
||||
|
||||
@@ -250,7 +250,7 @@ namespace Barotrauma.Items.Components
|
||||
int visibleElementCount = 0;
|
||||
foreach (var uiElement in uiElements)
|
||||
{
|
||||
if (!(uiElement.UserData is CustomInterfaceElement element)) { continue; }
|
||||
if (uiElement.UserData is not CustomInterfaceElement element) { continue; }
|
||||
bool visible = Screen.Selected == GameMain.SubEditorScreen || element.StatusEffects.Any() || element.HasPropertyName || (element.Connection != null && element.Connection.Wires.Count > 0);
|
||||
if (visible) { visibleElementCount++; }
|
||||
if (uiElement.Visible != visible)
|
||||
@@ -337,7 +337,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (uiElements[i] is GUITextBox tb)
|
||||
{
|
||||
tb.Text = customInterfaceElementList[i].Signal;
|
||||
tb.Text = Screen.Selected is { IsEditor: true } ?
|
||||
customInterfaceElementList[i].Signal :
|
||||
TextManager.Get(customInterfaceElementList[i].Signal).Value;
|
||||
}
|
||||
else if (uiElements[i] is GUINumberInput ni)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,6 @@ using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -119,6 +118,13 @@ namespace Barotrauma.Items.Components
|
||||
get { return sectionExtents; }
|
||||
}
|
||||
|
||||
public readonly record struct VisualSignal(
|
||||
float TimeSent,
|
||||
Color Color,
|
||||
int Direction);
|
||||
|
||||
private VisualSignal lastReceivedSignal;
|
||||
|
||||
public static Wire DraggingWire
|
||||
{
|
||||
get => draggingWire;
|
||||
@@ -126,13 +132,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
Sprite overrideSprite = null;
|
||||
foreach (var subElement in element.Elements())
|
||||
@@ -153,6 +157,35 @@ namespace Barotrauma.Items.Components
|
||||
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)
|
||||
{
|
||||
Draw(spriteBatch, editing, Vector2.Zero, itemDepth);
|
||||
@@ -166,20 +199,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 drawOffset = Vector2.Zero;
|
||||
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;
|
||||
Vector2 drawOffset = GetDrawOffset() + offset;
|
||||
|
||||
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);
|
||||
@@ -188,7 +208,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
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)
|
||||
@@ -270,6 +292,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ConnectionPanel.ShouldDebugDrawWiring)
|
||||
{
|
||||
DebugDraw(spriteBatch, alpha: 0.2f);
|
||||
}
|
||||
|
||||
if (!editing || !GameMain.SubEditorScreen.WiringMode) { return; }
|
||||
|
||||
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)
|
||||
{
|
||||
float angle = (float)Math.Sin(GameMain.GameScreen.GameTime * 2.0f + item.ID) * 0.2f;
|
||||
|
||||
@@ -46,6 +46,13 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool DebugWiring
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No)]
|
||||
public bool ShowDeadCharacters
|
||||
{
|
||||
@@ -111,6 +118,11 @@ namespace Barotrauma.Items.Components
|
||||
refEntity = item;
|
||||
}
|
||||
|
||||
if (equipper != null && equipper == Character.Controlled && DebugWiring)
|
||||
{
|
||||
ConnectionPanel.DebugWiringEnabledUntil = Timing.TotalTimeUnpaused + 0.5;
|
||||
}
|
||||
|
||||
thermalEffectState += deltaTime;
|
||||
thermalEffectState %= 10000.0f;
|
||||
|
||||
@@ -153,6 +165,11 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper, bool setTransform = true)
|
||||
{
|
||||
Unequip(dropper);
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
if (character == null) { return; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Lights;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Collision;
|
||||
using Microsoft.Xna.Framework;
|
||||
@@ -10,6 +11,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
private GUIMessageBox autodockingVerification;
|
||||
|
||||
private readonly ConvexHull[] convexHulls = new ConvexHull[2];
|
||||
|
||||
public Vector2 DrawSize
|
||||
{
|
||||
//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)
|
||||
{
|
||||
bool isDocked = msg.ReadBoolean();
|
||||
|
||||
@@ -1398,7 +1398,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Failed to read component state - " + components[componentIndex].GetType() + " is not IServerSerializable.");
|
||||
throw new Exception($"Failed to read component state - {components[componentIndex].GetType()} in item \"{Prefab.Identifier}\" is not IServerSerializable.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1411,7 +1411,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Failed to read inventory state - " + components[containerIndex].GetType() + " is not an ItemContainer.");
|
||||
throw new Exception($"Failed to read inventory state - {components[containerIndex].GetType()} in item \"{Prefab.Identifier}\" is not an ItemContainer.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1460,9 +1460,9 @@ namespace Barotrauma
|
||||
byte length = msg.ReadByte();
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
var statIdentifier = INetSerializableStruct.Read<ItemStatManager.TalentStatIdentifier>(msg);
|
||||
var statIdentifier = INetSerializableStruct.Read<TalentStatIdentifier>(msg);
|
||||
var statValue = msg.ReadSingle();
|
||||
StatManager.ApplyStat(statIdentifier, statValue);
|
||||
StatManager.ApplyStatDirect(statIdentifier, statValue);
|
||||
}
|
||||
break;
|
||||
case EventType.Upgrade:
|
||||
|
||||
@@ -9,15 +9,17 @@ namespace Barotrauma
|
||||
{
|
||||
private LightSource lightSource;
|
||||
|
||||
partial void UpdateProjSpecific(float growModifier)
|
||||
private float particleTimer;
|
||||
|
||||
partial void UpdateProjSpecific(float growModifier, float deltaTime)
|
||||
{
|
||||
if (this is DummyFireSource)
|
||||
{
|
||||
EmitParticles(size, WorldPosition, hull, growModifier, null);
|
||||
EmitParticles(size, WorldPosition, deltaTime, hull, growModifier, null);
|
||||
}
|
||||
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);
|
||||
@@ -25,23 +27,29 @@ namespace Barotrauma
|
||||
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(
|
||||
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(
|
||||
particlePos.X - (worldPosition.X + size.X / 2.0f),
|
||||
Math.Max((float)Math.Sqrt(size.X) * Rand.Range(0.0f, 15.0f) * growModifier, 0.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);
|
||||
|
||||
if (particle == null) { continue; }
|
||||
@@ -54,7 +62,7 @@ namespace Barotrauma
|
||||
if (Rand.Int(5) == 1)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Barotrauma
|
||||
{
|
||||
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;
|
||||
particlePos = intersection;
|
||||
|
||||
@@ -93,13 +93,16 @@ namespace Barotrauma.Lights
|
||||
|
||||
private readonly int thickness;
|
||||
|
||||
public bool IsExteriorWall;
|
||||
|
||||
public VertexPositionColor[] ShadowVertices { get; private set; }
|
||||
public VertexPositionTexture[] PenumbraVertices { get; private set; }
|
||||
public int ShadowVertexCount { 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>();
|
||||
|
||||
public MapEntity ParentEntity { get; private set; }
|
||||
@@ -130,7 +133,7 @@ namespace Barotrauma.Lights
|
||||
|
||||
public Rectangle BoundingBox { get; private set; }
|
||||
|
||||
public ConvexHull(Rectangle rect, bool? isHorizontal, MapEntity parent)
|
||||
public ConvexHull(Rectangle rect, bool isHorizontal, MapEntity parent)
|
||||
{
|
||||
shadowEffect ??= new BasicEffect(GameMain.Instance.GraphicsDevice)
|
||||
{
|
||||
@@ -150,15 +153,15 @@ namespace Barotrauma.Lights
|
||||
|
||||
BoundingBox = rect;
|
||||
|
||||
this.isHorizontal = isHorizontal ?? BoundingBox.Width > BoundingBox.Height;
|
||||
this.isHorizontal = isHorizontal;
|
||||
if (ParentEntity is Structure structure)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(!structure.Removed);
|
||||
Debug.Assert(!structure.Removed);
|
||||
isHorizontal = structure.IsHorizontal;
|
||||
}
|
||||
else if (ParentEntity is Item item)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(!item.Removed);
|
||||
Debug.Assert(!item.Removed);
|
||||
var door = item.GetComponent<Door>();
|
||||
if (door != null) { isHorizontal = door.IsHorizontal; }
|
||||
}
|
||||
@@ -205,44 +208,97 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
if (ch == this) { return; }
|
||||
|
||||
//hide segments that are roughly at the some position as some other segment (e.g. the ends of two adjacent wall pieces)
|
||||
float mergeDist = MathHelper.Clamp(ch.thickness * 0.55f, 16, 512);
|
||||
mergeDist = Math.Min(mergeDist, Vector2.Distance(losVertices[0].Pos, losVertices[1].Pos) / 2);
|
||||
//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)
|
||||
{
|
||||
mergeDistParallel = Math.Max(mergeDistParallel, MaxMergeLosVerticesDist.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
if (!inflatedAABB.Contains(losVertices[0].Pos) &&
|
||||
!inflatedAABB.Contains(losVertices[1].Pos))
|
||||
{
|
||||
mergeDistParallel = Math.Min(mergeDistParallel, Vector2.Distance(losVertices[0].Pos, losVertices[1].Pos) * 0.5f);
|
||||
}
|
||||
}
|
||||
//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);
|
||||
|
||||
float mergeDistSqr = mergeDist * mergeDist;
|
||||
Vector2 center = (losVertices[0].Pos + losVertices[1].Pos) / 2;
|
||||
|
||||
bool changed = false;
|
||||
for (int i = 0; i < losVertices.Length; i++)
|
||||
{
|
||||
//find the closest point on the other convex hull segment
|
||||
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 (Vector2.DistanceSquared(closest, losVertices[i].Pos) > mergeDistSqr) { continue; }
|
||||
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,
|
||||
out Vector2 intersection))
|
||||
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))
|
||||
{
|
||||
if (Vector2.DistanceSquared(intersection, losVertices[i].Pos) < mergeDistSqr ||
|
||||
Vector2.DistanceSquared(intersection, closest) < 16.0f * 16.0f)
|
||||
{
|
||||
closest = intersection;
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -253,6 +309,13 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Matrix rotationMatrix =
|
||||
@@ -347,6 +410,7 @@ namespace Barotrauma.Lights
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
losVertices[i] = new SegmentPoint(losPoints[i], this);
|
||||
losOffsets[i] = Vector2.Zero;
|
||||
}
|
||||
|
||||
overlappingHulls.Clear();
|
||||
@@ -612,8 +676,11 @@ namespace Barotrauma.Lights
|
||||
vertexPos0 += ParentEntity.Submarine.DrawPosition;
|
||||
vertexPos1 += ParentEntity.Submarine.DrawPosition;
|
||||
}
|
||||
Vector2 viewTargetPos = LightManager.ViewTarget.WorldPosition;
|
||||
float alpha = IsSegmentFacing(vertexPos0, vertexPos1, viewTargetPos) ? 1.0f : 0.5f;
|
||||
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);
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
|
||||
private class RayCastTask
|
||||
private sealed class RayCastTask
|
||||
{
|
||||
public LightSource LightSource;
|
||||
public Vector2 DrawPos;
|
||||
@@ -298,7 +298,8 @@ namespace Barotrauma.Lights
|
||||
range *
|
||||
((Character.Controlled?.Submarine != null && light.ParentSub == Character.Controlled?.Submarine) ? 2.0f : 1.0f) *
|
||||
(light.CastShadows ? 10.0f : 1.0f) *
|
||||
(light.LightSourceParams.OverrideLightSpriteAlpha ?? (light.Color.A / 255.0f));
|
||||
(light.LightSourceParams.OverrideLightSpriteAlpha ?? (light.Color.A / 255.0f)) *
|
||||
light.PriorityMultiplier;
|
||||
}
|
||||
|
||||
//find the lights with an active light volume
|
||||
@@ -477,6 +478,17 @@ namespace Barotrauma.Lights
|
||||
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;
|
||||
|
||||
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
|
||||
@@ -686,19 +698,42 @@ namespace Barotrauma.Lights
|
||||
if (LosEnabled && LosMode != LosMode.None && ViewTarget != null)
|
||||
{
|
||||
Vector2 pos = ViewTarget.DrawPosition;
|
||||
if (ViewTarget is Character character &&
|
||||
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);
|
||||
|
||||
Matrix shadowTransform = cam.ShaderTransform
|
||||
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
|
||||
|
||||
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)
|
||||
{
|
||||
List<VertexPositionColor> shadowVerts = new List<VertexPositionColor>();
|
||||
@@ -706,7 +741,6 @@ namespace Barotrauma.Lights
|
||||
foreach (ConvexHull convexHull in convexHulls)
|
||||
{
|
||||
if (!convexHull.Enabled || !convexHull.Intersects(camView)) { continue; }
|
||||
if (LosMode == LosMode.BlockOutsideView && !convexHull.IsExteriorWall) { continue; };
|
||||
|
||||
Vector2 relativeLightPos = pos;
|
||||
if (convexHull.ParentEntity?.Submarine != null) { relativeLightPos -= convexHull.ParentEntity.Submarine.Position; }
|
||||
@@ -744,13 +778,14 @@ namespace Barotrauma.Lights
|
||||
|
||||
public void DebugDrawLos(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
if (ViewTarget == null) { return; }
|
||||
Vector2 pos = ViewTarget?.Position ?? cam.Position;
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cam.Transform);
|
||||
var convexHulls = ConvexHull.GetHullsInRange(ViewTarget.Position, cam.WorldView.Width * 0.75f, ViewTarget?.Submarine);
|
||||
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();
|
||||
|
||||
@@ -53,10 +53,6 @@ namespace Barotrauma.Lights
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = -360, MaxValueFloat = 360, ValueStep = 1, DecimalCount = 0)]
|
||||
public float Rotation { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, "Directional lights only shine in \"one direction\", meaning no shadows are cast behind them."+
|
||||
" Note that this does not affect how the light texture is drawn: if you want something like a conical spotlight, you should use an appropriate texture for that.")]
|
||||
public bool Directional { get; set; }
|
||||
|
||||
public Vector2 GetOffset() => Vector2.Transform(Offset, Matrix.CreateRotationZ(MathHelper.ToRadians(Rotation)));
|
||||
|
||||
private float flicker;
|
||||
@@ -233,6 +229,7 @@ namespace Barotrauma.Lights
|
||||
|
||||
//do we need to recalculate the vertices of the light volume
|
||||
private bool needsRecalculation;
|
||||
private bool needsRecalculationWhenUpToDate;
|
||||
public bool NeedsRecalculation
|
||||
{
|
||||
get { return needsRecalculation; }
|
||||
@@ -246,9 +243,15 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
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
|
||||
public float LastRecalculationTime { get; private set; }
|
||||
|
||||
@@ -309,8 +312,6 @@ namespace Barotrauma.Lights
|
||||
if (Math.Abs(value - rotation) < 0.001f) { return; }
|
||||
rotation = value;
|
||||
|
||||
dir = new Vector2(MathF.Cos(rotation), -MathF.Sin(rotation));
|
||||
|
||||
if (Math.Abs(rotation - prevCalculatedRotation) < RotationRecalculationThreshold && vertices != null)
|
||||
{
|
||||
return;
|
||||
@@ -321,8 +322,6 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 dir = Vector2.UnitX;
|
||||
|
||||
private Vector2 _spriteScale = Vector2.One;
|
||||
|
||||
public Vector2 SpriteScale
|
||||
@@ -396,6 +395,8 @@ namespace Barotrauma.Lights
|
||||
|
||||
public float Priority;
|
||||
|
||||
public float PriorityMultiplier = 1.0f;
|
||||
|
||||
private Vector2 lightTextureTargetSize;
|
||||
|
||||
public Vector2 LightTextureTargetSize
|
||||
@@ -444,7 +445,7 @@ namespace Barotrauma.Lights
|
||||
public bool Enabled = true;
|
||||
|
||||
private readonly ISerializableEntity conditionalTarget;
|
||||
private readonly PropertyConditional.LogicalOperatorType logicalOperator;
|
||||
private readonly PropertyConditional.Comparison comparison;
|
||||
private readonly List<PropertyConditional> conditionals = new List<PropertyConditional>();
|
||||
|
||||
public LightSource(ContentXElement element, ISerializableEntity conditionalTarget = null)
|
||||
@@ -452,8 +453,11 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
lightSourceParams = new LightSourceParams(element);
|
||||
CastShadows = element.GetAttributeBool("castshadows", true);
|
||||
logicalOperator = element.GetAttributeEnum(nameof(logicalOperator),
|
||||
element.GetAttributeEnum("comparison", logicalOperator));
|
||||
string comparison = element.GetAttributeString("comparison", null);
|
||||
if (comparison != null)
|
||||
{
|
||||
Enum.TryParse(comparison, ignoreCase: true, out this.comparison);
|
||||
}
|
||||
|
||||
if (lightSourceParams.DeformableLightSpriteElement != null)
|
||||
{
|
||||
@@ -466,7 +470,13 @@ namespace Barotrauma.Lights
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "conditional":
|
||||
conditionals.AddRange(PropertyConditional.FromXElement(subElement));
|
||||
foreach (XAttribute attribute in subElement.Attributes())
|
||||
{
|
||||
if (PropertyConditional.IsValid(attribute))
|
||||
{
|
||||
conditionals.Add(new PropertyConditional(attribute));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -529,32 +539,11 @@ namespace Barotrauma.Lights
|
||||
var fullChList = ConvexHull.HullLists.FirstOrDefault(chList => chList.Submarine == sub);
|
||||
if (fullChList == null) { return; }
|
||||
|
||||
//used to check whether the lightsource hits the target hull if the light is directional
|
||||
Vector2 ray = new Vector2(dir.X, -dir.Y) * TextureRange;
|
||||
Vector2 normal = new Vector2(-ray.Y, ray.X);
|
||||
|
||||
chList.List.Clear();
|
||||
foreach (var convexHull in fullChList.List)
|
||||
{
|
||||
if (!convexHull.Enabled) { continue; }
|
||||
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, convexHull.BoundingBox)) { continue; }
|
||||
if (lightSourceParams.Directional && false)
|
||||
{
|
||||
Rectangle bounds = convexHull.BoundingBox;
|
||||
//invert because GetLineRectangleIntersection uses the messed up rects that start from top-left
|
||||
bounds.Y -= bounds.Height;
|
||||
|
||||
//the ray can't hit if
|
||||
// center is in the opposite direction from the ray (cheapest check first)
|
||||
if (Vector2.Dot(ray, convexHull.BoundingBox.Center.ToVector2() - lightPos) <= 0 &&
|
||||
/*ray doesn't hit the convex hull*/
|
||||
!MathUtils.GetLineRectangleIntersection(lightPos, lightPos + ray, bounds, out _) &&
|
||||
/*normal vectors of the ray don't hit the convex hull */
|
||||
!MathUtils.GetLineRectangleIntersection(lightPos + normal, lightPos - normal, bounds, out _))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
chList.List.Add(convexHull);
|
||||
}
|
||||
chList.IsHidden.RemoveWhere(ch => !chList.List.Contains(ch));
|
||||
@@ -704,7 +693,7 @@ namespace Barotrauma.Lights
|
||||
lock (mutex)
|
||||
{
|
||||
hull.RefreshWorldPositions();
|
||||
hull.GetVisibleSegments(drawPos, visibleSegments);
|
||||
hull.GetVisibleSegments(drawPos, visibleSegments);
|
||||
foreach (var visibleSegment in visibleSegments)
|
||||
{
|
||||
if (visibleSegment.ConvexHull?.ParentEntity?.Submarine != null)
|
||||
@@ -713,7 +702,6 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
foreach (ConvexHull hull in chList.List)
|
||||
@@ -812,7 +800,7 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
else
|
||||
{
|
||||
intersects = MathUtils.GetLineIntersection(p1a, p1b, p2a, p2b, out intersection);
|
||||
intersects = MathUtils.GetLineSegmentIntersection(p1a, p1b, p2a, p2b, out intersection);
|
||||
}
|
||||
|
||||
if (intersects)
|
||||
@@ -1006,7 +994,7 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
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)
|
||||
@@ -1041,9 +1029,11 @@ namespace Barotrauma.Lights
|
||||
|
||||
Vector2 drawPos = calculatedDrawPos;
|
||||
|
||||
float cosAngle = (float)Math.Cos(Rotation);
|
||||
float sinAngle = -(float)Math.Sin(Rotation);
|
||||
|
||||
Vector2 uvOffset = Vector2.Zero;
|
||||
Vector2 overrideTextureDims = Vector2.One;
|
||||
Vector2 dir = this.dir;
|
||||
if (OverrideLightTexture != null)
|
||||
{
|
||||
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
|
||||
@@ -1052,7 +1042,8 @@ namespace Barotrauma.Lights
|
||||
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
|
||||
{
|
||||
origin.X = OverrideLightTexture.SourceRect.Width - origin.X;
|
||||
dir = -dir;
|
||||
cosAngle = -cosAngle;
|
||||
sinAngle = -sinAngle;
|
||||
}
|
||||
if (LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = OverrideLightTexture.SourceRect.Height - origin.Y; }
|
||||
uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
|
||||
@@ -1125,8 +1116,8 @@ namespace Barotrauma.Lights
|
||||
//calculate texture coordinates based on the light's rotation
|
||||
Vector2 originDiff = diff;
|
||||
|
||||
diff.X = originDiff.X * dir.X - originDiff.Y * dir.Y;
|
||||
diff.Y = originDiff.X * dir.Y + originDiff.Y * dir.X;
|
||||
diff.X = originDiff.X * cosAngle - originDiff.Y * sinAngle;
|
||||
diff.Y = originDiff.X * sinAngle + originDiff.Y * cosAngle;
|
||||
diff *= (overrideTextureDims / OverrideLightTexture.size);// / (1.0f - Math.Max(Math.Abs(uvOffset.X), Math.Abs(uvOffset.Y)));
|
||||
diff += uvOffset;
|
||||
}
|
||||
@@ -1231,6 +1222,9 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
drawPos.Y = -drawPos.Y;
|
||||
|
||||
float cosAngle = (float)Math.Cos(Rotation);
|
||||
float sinAngle = -(float)Math.Sin(Rotation);
|
||||
|
||||
float bounds = TextureRange;
|
||||
|
||||
if (OverrideLightTexture != null)
|
||||
@@ -1242,8 +1236,8 @@ namespace Barotrauma.Lights
|
||||
origin /= Math.Max(overrideTextureDims.X, overrideTextureDims.Y);
|
||||
origin *= TextureRange;
|
||||
|
||||
drawPos.X += origin.X * dir.Y + origin.Y * dir.X;
|
||||
drawPos.Y += origin.X * dir.X + origin.Y * dir.Y;
|
||||
drawPos.X += origin.X * sinAngle + origin.Y * cosAngle;
|
||||
drawPos.Y += origin.X * cosAngle + origin.Y * sinAngle;
|
||||
}
|
||||
|
||||
//add a square-shaped boundary to make sure we've got something to construct the triangles from
|
||||
@@ -1349,7 +1343,7 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
if (conditionals.None()) { return; }
|
||||
if (conditionalTarget == null) { return; }
|
||||
if (logicalOperator == PropertyConditional.LogicalOperatorType.And)
|
||||
if (comparison == PropertyConditional.Comparison.And)
|
||||
{
|
||||
Enabled = conditionals.All(c => c.Matches(conditionalTarget));
|
||||
}
|
||||
@@ -1405,7 +1399,9 @@ namespace Barotrauma.Lights
|
||||
CalculateLightVertices(verts);
|
||||
|
||||
LastRecalculationTime = (float)Timing.TotalTime;
|
||||
NeedsRecalculation = false;
|
||||
NeedsRecalculation = needsRecalculationWhenUpToDate;
|
||||
needsRecalculationWhenUpToDate = false;
|
||||
|
||||
state = LightVertexState.UpToDate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +56,12 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (GameMain.SoundManager?.Disabled ?? true) { return null; }
|
||||
|
||||
var filename = element.GetAttributeContentPath("file") ?? element.GetAttributeContentPath("sound");
|
||||
|
||||
if (filename is null)
|
||||
{
|
||||
string errorMsg = "Error when loading round sound (" + element + ") - file path not set";
|
||||
@@ -70,7 +70,11 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
@@ -99,7 +103,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
RoundSound newSound = new RoundSound(element, existingSound);
|
||||
|
||||
if (filename is not null && !newSound.Stream)
|
||||
{
|
||||
roundSoundByPath.TryAdd(filename.FullPath, newSound);
|
||||
}
|
||||
roundSounds.Add(newSound);
|
||||
return newSound;
|
||||
}
|
||||
@@ -124,24 +131,14 @@ namespace Barotrauma
|
||||
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()
|
||||
{
|
||||
for (int i = roundSounds.Count - 1; i >= 0; i--)
|
||||
foreach (var roundSound in roundSounds)
|
||||
{
|
||||
Remove(roundSounds[i]);
|
||||
roundSound.Sound?.Dispose();
|
||||
}
|
||||
roundSounds.Clear();
|
||||
roundSoundByPath.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,11 +58,8 @@ namespace Barotrauma
|
||||
convexHulls ??= new List<ConvexHull>();
|
||||
var h = new ConvexHull(
|
||||
new Rectangle((position - size / 2).ToPoint(), size.ToPoint()),
|
||||
IsHorizontal,
|
||||
this)
|
||||
{
|
||||
IsExteriorWall = IsExteriorWall
|
||||
};
|
||||
IsHorizontal,
|
||||
this);
|
||||
if (Math.Abs(rotation) > 0.001f)
|
||||
{
|
||||
h.Rotate(position, rotation);
|
||||
@@ -501,7 +498,7 @@ namespace Barotrauma
|
||||
|
||||
private bool ConditionalMatches(PropertyConditional conditional)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(conditional.TargetItemComponent))
|
||||
if (!string.IsNullOrEmpty(conditional.TargetItemComponentName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -116,11 +116,11 @@ namespace Barotrauma
|
||||
|
||||
foreach (MapEntity e in entitiesToRender)
|
||||
{
|
||||
if (!e.DrawOverWater) continue;
|
||||
if (!e.DrawOverWater) { continue; }
|
||||
|
||||
if (predicate != null)
|
||||
{
|
||||
if (!predicate(e)) continue;
|
||||
if (!predicate(e)) { continue; }
|
||||
}
|
||||
|
||||
e.Draw(spriteBatch, editing, false);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -18,7 +19,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
List<Submarine> subsToMove = submarine.GetConnectedSubs();
|
||||
var subsToMove = submarine.GetConnectedSubs();
|
||||
foreach (Submarine dockedSub in subsToMove)
|
||||
{
|
||||
if (dockedSub == submarine) { continue; }
|
||||
@@ -51,7 +52,6 @@ namespace Barotrauma
|
||||
sub.PhysicsBody.SetTransformIgnoreContacts(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (closestSub != null && subsToMove.Contains(closestSub))
|
||||
{
|
||||
GameMain.GameScreen.Cam.Position += moveAmount;
|
||||
|
||||
@@ -67,6 +67,10 @@ namespace Barotrauma
|
||||
else if (ConnectedDoor != null)
|
||||
{
|
||||
sprite = iconSprites["Door"];
|
||||
if (ConnectedDoor.IsHorizontal && Ladders == null)
|
||||
{
|
||||
clr = Color.Yellow;
|
||||
}
|
||||
}
|
||||
else if (Ladders != null)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -16,6 +15,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
sealed class GameClient : NetworkMember
|
||||
{
|
||||
public static readonly TimeSpan CampaignSaveTransferTimeOut = new TimeSpan(0, 0, seconds: 100);
|
||||
//this should be longer than CampaignSaveTransferTimeOut - we shouldn't give up starting the round if we're still waiting for the save file
|
||||
public static readonly TimeSpan LevelTransitionTimeOut = new TimeSpan(0, 0, seconds: 150);
|
||||
|
||||
public override bool IsClient => true;
|
||||
public override bool IsServer => false;
|
||||
|
||||
@@ -514,6 +517,7 @@ namespace Barotrauma.Networking
|
||||
DisplayInLoadingScreens = true
|
||||
};
|
||||
Quit();
|
||||
GUI.DisableHUD = false;
|
||||
GameMain.ServerListScreen.Select();
|
||||
return;
|
||||
}
|
||||
@@ -931,7 +935,7 @@ namespace Barotrauma.Networking
|
||||
", level value count: " + levelEqualityCheckValues.Count +
|
||||
", seed: " + Level.Loaded.Seed +
|
||||
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation + ")" +
|
||||
", mirrored: " + Level.Loaded.Mirrored + "). Round init status: {roundInitStatus}." + campaignErrorInfo;
|
||||
", mirrored: " + Level.Loaded.Mirrored + "). Round init status: " + roundInitStatus + "." + campaignErrorInfo;
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
@@ -1487,16 +1491,17 @@ namespace Barotrauma.Networking
|
||||
NetIdUtils.IdMoreRecent(campaignSaveID, campaign.PendingSaveID))
|
||||
{
|
||||
campaign.PendingSaveID = campaignSaveID;
|
||||
DateTime saveFileTimeOut = DateTime.Now + new TimeSpan(0, 0, 60);
|
||||
DateTime saveFileTimeOut = DateTime.Now + CampaignSaveTransferTimeOut;
|
||||
while (NetIdUtils.IdMoreRecent(campaignSaveID, campaign.LastSaveID))
|
||||
{
|
||||
if (DateTime.Now > saveFileTimeOut)
|
||||
{
|
||||
GameStarted = true;
|
||||
DebugConsole.ThrowError("Failed to start campaign round (timed out while waiting for the up-to-date save file).");
|
||||
new GUIMessageBox(TextManager.Get("error"), TextManager.Get("campaignsavetransfer.timeout"));
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
roundInitStatus = RoundInitStatus.Interrupted;
|
||||
yield return CoroutineStatus.Failure;
|
||||
//use success status, even though this is a failure (no need to show a console error because we show it in the message box)
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
}
|
||||
@@ -1712,7 +1717,7 @@ namespace Barotrauma.Networking
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
if (GameMain.GameSession != null) { GameMain.GameSession.EndRound(endMessage, traitorResults, transitionType); }
|
||||
GameMain.GameSession?.EndRound(endMessage, traitorResults, transitionType);
|
||||
|
||||
ServerSettings.ServerDetailsChanged = true;
|
||||
|
||||
@@ -2872,12 +2877,14 @@ namespace Barotrauma.Networking
|
||||
ClientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public bool SpectateClicked(GUIButton button, object userData)
|
||||
public bool SpectateClicked(GUIButton button, object _)
|
||||
{
|
||||
MultiPlayerCampaign campaign =
|
||||
MultiPlayerCampaign campaign =
|
||||
GameMain.NetLobbyScreen.SelectedMode == GameMain.GameSession?.GameMode.Preset ?
|
||||
GameMain.GameSession?.GameMode as MultiPlayerCampaign : null;
|
||||
if (campaign != null && campaign.LastSaveID < campaign.PendingSaveID)
|
||||
|
||||
if (FileReceiver.ActiveTransfers.Any(t => t.FileType == FileTransferType.CampaignSave) ||
|
||||
(campaign != null && NetIdUtils.IdMoreRecent(campaign.PendingSaveID, campaign.LastSaveID)))
|
||||
{
|
||||
new GUIMessageBox("", TextManager.Get("campaignfiletransferinprogress"));
|
||||
return false;
|
||||
@@ -3063,8 +3070,12 @@ namespace Barotrauma.Networking
|
||||
if (votingInterface != null)
|
||||
{
|
||||
votingInterface.Update(deltaTime);
|
||||
if (!votingInterface.VoteRunning)
|
||||
if (!votingInterface.VoteRunning || votingInterface.TimedOut)
|
||||
{
|
||||
if (votingInterface.TimedOut)
|
||||
{
|
||||
DebugConsole.AddWarning($"Voting interface timed out.");
|
||||
}
|
||||
votingInterface.Remove();
|
||||
votingInterface = null;
|
||||
}
|
||||
|
||||
+18
-1
@@ -111,8 +111,25 @@ namespace Barotrauma.Networking
|
||||
? NetworkConnection.TimeoutThresholdInGame
|
||||
: NetworkConnection.TimeoutThreshold;
|
||||
|
||||
IReadMessage inc = new ReadOnlyMessage(data, false, 0, dataLength, ServerConnection);
|
||||
try
|
||||
{
|
||||
IReadMessage inc = new ReadOnlyMessage(data, false, 0, dataLength, ServerConnection);
|
||||
ProcessP2PData(inc);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = $"Client failed to read an incoming P2P message. {{{e}}}\n{e.StackTrace.CleanupStackTrace()}";
|
||||
GameAnalyticsManager.AddErrorEventOnce($"SteamP2PClientPeer.OnP2PData:ClientReadException{e.TargetSite}", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#else
|
||||
if (GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.ThrowError(errorMsg); }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessP2PData(IReadMessage inc)
|
||||
{
|
||||
var (deliveryMethod, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
|
||||
|
||||
if (!packetHeader.IsServerMessage()) { return; }
|
||||
|
||||
+22
-5
@@ -141,15 +141,31 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (remotePeer.DisconnectTime != null) { return; }
|
||||
|
||||
var peerPacketHeaders = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
|
||||
|
||||
PacketHeader packetHeader = peerPacketHeaders.PacketHeader;
|
||||
try
|
||||
{
|
||||
ProcessP2PData(steamId, remotePeer, inc);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = $"Server failed to read an incoming P2P message. {{{e}}}\n{e.StackTrace.CleanupStackTrace()}";
|
||||
GameAnalyticsManager.AddErrorEventOnce($"SteamP2POwnerPeer.OnP2PData:OwnerReadException{e.TargetSite}", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#else
|
||||
if (GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.ThrowError(errorMsg); }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (!remotePeer.Authenticated && !remotePeer.Authenticating && packetHeader.IsConnectionInitializationStep())
|
||||
private void ProcessP2PData(ulong steamId, RemotePeer remotePeer, IReadMessage inc)
|
||||
{
|
||||
var (deliveryMethod, packetHeader, connectionInitialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
|
||||
|
||||
if (remotePeer is { Authenticated: false, Authenticating: false } && packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
remotePeer.DisconnectTime = null;
|
||||
|
||||
ConnectionInitialization initialization = peerPacketHeaders.Initialization ?? throw new Exception("Initialization step missing");
|
||||
ConnectionInitialization initialization = connectionInitialization ?? throw new Exception("Initialization step missing");
|
||||
if (initialization == ConnectionInitialization.SteamTicketAndVersion)
|
||||
{
|
||||
remotePeer.Authenticating = true;
|
||||
@@ -181,6 +197,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
ForwardToServerProcess(outMsg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ref struct LobbyDataChangedEventHandler
|
||||
private readonly struct LobbyDataChangedEventHandler : IDisposable
|
||||
{
|
||||
private readonly Action<Lobby> action;
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ namespace Barotrauma.Networking
|
||||
int botSpawnMode = 0,
|
||||
bool? useRespawnShuttle = null)
|
||||
{
|
||||
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
|
||||
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) { return; }
|
||||
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
|
||||
|
||||
@@ -253,12 +253,9 @@ namespace Barotrauma.Networking
|
||||
//in push-to-talk mode, InputType.Voice uses the active chat mode
|
||||
bool usingActiveMode = PlayerInput.KeyDown(InputType.Voice);
|
||||
bool pttDown = (usingActiveMode || usingLocalMode || usingRadioMode) && GUI.KeyboardDispatcher.Subscriber == null;
|
||||
if (pttDown || captureTimer <= 0)
|
||||
{
|
||||
ForceLocal = (usingActiveMode && GameMain.ActiveChatMode == ChatMode.Local) || usingLocalMode;
|
||||
}
|
||||
if (pttDown)
|
||||
{
|
||||
ForceLocal = (usingActiveMode && GameMain.ActiveChatMode == ChatMode.Local) || usingLocalMode;
|
||||
allowEnqueue = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,17 +115,20 @@ namespace Barotrauma.Networking
|
||||
float speechImpedimentMultiplier = 1.0f - client.Character.SpeechImpediment / 100.0f;
|
||||
bool spectating = Character.Controlled == null;
|
||||
float rangeMultiplier = spectating ? 2.0f : 1.0f;
|
||||
WifiComponent radio = null;
|
||||
WifiComponent senderRadio = null;
|
||||
var messageType =
|
||||
!client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) && ChatMessage.CanUseRadio(Character.Controlled) ?
|
||||
ChatMessageType.Radio : ChatMessageType.Default;
|
||||
!client.VoipQueue.ForceLocal &&
|
||||
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.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters;
|
||||
client.RadioNoise = 0.0f;
|
||||
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)
|
||||
{
|
||||
//noise starts increasing exponentially after 40% range
|
||||
|
||||
@@ -348,11 +348,13 @@ namespace Barotrauma
|
||||
switch (voteType)
|
||||
{
|
||||
case VoteType.PurchaseAndSwitchSub:
|
||||
GameMain.GameSession.PurchaseSubmarine(subInfo);
|
||||
GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems);
|
||||
if (GameMain.GameSession.TryPurchaseSubmarine(subInfo))
|
||||
{
|
||||
GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems);
|
||||
}
|
||||
break;
|
||||
case VoteType.PurchaseSub:
|
||||
GameMain.GameSession.PurchaseSubmarine(subInfo);
|
||||
GameMain.GameSession.TryPurchaseSubmarine(subInfo);
|
||||
break;
|
||||
case VoteType.SwitchSub:
|
||||
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.Graphics;
|
||||
using System;
|
||||
@@ -48,19 +49,23 @@ namespace Barotrauma.Particles
|
||||
|
||||
private Vector2 drawPosition;
|
||||
private float drawRotation;
|
||||
|
||||
private Vector2 colliderRadius;
|
||||
|
||||
private Hull currentHull;
|
||||
|
||||
private List<Gap> hullGaps;
|
||||
|
||||
private bool hasSubEmitters;
|
||||
private List<ParticleEmitter> subEmitters = new List<ParticleEmitter>();
|
||||
private readonly List<ParticleEmitter> subEmitters = new List<ParticleEmitter>();
|
||||
|
||||
private float animState;
|
||||
private int animFrame;
|
||||
|
||||
private float collisionUpdateTimer;
|
||||
|
||||
private bool changesSize;
|
||||
|
||||
public bool HighQualityCollisionDetection;
|
||||
|
||||
public Vector4 ColorMultiplier;
|
||||
@@ -127,7 +132,10 @@ namespace Barotrauma.Particles
|
||||
position = (tracerPoints.Item1 + tracerPoints.Item2) / 2;
|
||||
}
|
||||
|
||||
RefreshColliderSize();
|
||||
|
||||
sizeChange = prefab.SizeChangeMin + (prefab.SizeChangeMax - prefab.SizeChangeMin) * Rand.Range(0.0f, 1.0f);
|
||||
changesSize = !sizeChange.NearlyEquals(Vector2.Zero);
|
||||
|
||||
this.position = position;
|
||||
prevPosition = position;
|
||||
@@ -256,8 +264,12 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
}
|
||||
|
||||
size.X += sizeChange.X * deltaTime;
|
||||
size.Y += sizeChange.Y * deltaTime;
|
||||
if (changesSize)
|
||||
{
|
||||
size.X += sizeChange.X * deltaTime;
|
||||
size.Y += sizeChange.Y * deltaTime;
|
||||
RefreshColliderSize();
|
||||
}
|
||||
|
||||
if (UseMiddleColor)
|
||||
{
|
||||
@@ -344,11 +356,11 @@ namespace Barotrauma.Particles
|
||||
{
|
||||
Rectangle hullRect = currentHull.WorldRect;
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -378,11 +390,11 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -431,6 +443,13 @@ namespace Barotrauma.Particles
|
||||
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)
|
||||
{
|
||||
Vector2 relativeVel = velocity;
|
||||
@@ -475,11 +494,11 @@ namespace Barotrauma.Particles
|
||||
{
|
||||
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
|
||||
{
|
||||
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.Y *= (1.0f - prefab.Friction);
|
||||
@@ -488,11 +507,11 @@ namespace Barotrauma.Particles
|
||||
{
|
||||
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
|
||||
{
|
||||
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);
|
||||
@@ -513,26 +532,26 @@ namespace Barotrauma.Particles
|
||||
|
||||
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.Y = -velocity.Y * prefab.Restitution;
|
||||
}
|
||||
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.Y = -velocity.Y * prefab.Restitution;
|
||||
}
|
||||
|
||||
if (position.X < center.X)
|
||||
{
|
||||
position.X = hullRect.X - prefab.CollisionRadius;
|
||||
position.X = hullRect.X - colliderRadius.X;
|
||||
velocity.X = -velocity.X * prefab.Restitution;
|
||||
velocity.Y *= (1.0f - prefab.Friction);
|
||||
}
|
||||
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.Y *= (1.0f - prefab.Friction);
|
||||
}
|
||||
@@ -559,11 +578,12 @@ namespace Barotrauma.Particles
|
||||
|
||||
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,
|
||||
new Vector2(drawPosition.X, -drawPosition.Y),
|
||||
drawPos,
|
||||
currColor * (currColor.A / 255.0f),
|
||||
prefab.Sprites[spriteIndex].Origin, drawRotation,
|
||||
drawSize, SpriteEffects.None, prefab.Sprites[spriteIndex].Depth);
|
||||
@@ -571,11 +591,23 @@ namespace Barotrauma.Particles
|
||||
else
|
||||
{
|
||||
prefab.Sprites[spriteIndex].Draw(spriteBatch,
|
||||
new Vector2(drawPosition.X, -drawPosition.Y),
|
||||
drawPos,
|
||||
currColor * (currColor.A / 255.0f),
|
||||
prefab.Sprites[spriteIndex].Origin, drawRotation,
|
||||
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.")]
|
||||
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.")]
|
||||
public bool UseCollision { get; private set; }
|
||||
|
||||
|
||||
+4
-8
@@ -18,7 +18,7 @@ namespace Barotrauma
|
||||
public CharacterInfo.AppearanceCustomizationMenu[] CharacterMenus { get; private set; }
|
||||
|
||||
private GUIButton nextButton;
|
||||
private GUILayoutGroup characterInfoColumns;
|
||||
private GUIListBox characterInfoColumns;
|
||||
|
||||
public SinglePlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<SubmarineInfo> submarines, IEnumerable<CampaignMode.SaveInfo> saveFiles = null)
|
||||
: base(newGameContainer, loadGameContainer)
|
||||
@@ -249,11 +249,7 @@ namespace Barotrauma
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.04f), secondPageLayout.RectTransform),
|
||||
TextManager.Get("Crew"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.TopLeft);
|
||||
|
||||
characterInfoColumns = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.86f), secondPageLayout.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.01f
|
||||
};
|
||||
characterInfoColumns = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.86f), secondPageLayout.RectTransform), isHorizontal: true);
|
||||
|
||||
var secondPageButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f),
|
||||
secondPageLayout.RectTransform), childAnchor: Anchor.BottomLeft, isHorizontal: true)
|
||||
@@ -306,8 +302,8 @@ namespace Barotrauma
|
||||
|
||||
for (int i = 0; i < characterInfos.Count; i++)
|
||||
{
|
||||
var subLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f / characterInfos.Count, 1.0f),
|
||||
characterInfoColumns.RectTransform));
|
||||
var subLayout = new GUILayoutGroup(new RectTransform(new Vector2(Math.Max(1.0f / characterInfos.Count, 0.33f), 1.0f),
|
||||
characterInfoColumns.Content.RectTransform));
|
||||
|
||||
var (characterInfo, job) = characterInfos[i];
|
||||
|
||||
|
||||
@@ -202,18 +202,21 @@ namespace Barotrauma
|
||||
case CampaignMode.InteractionType.PurchaseSub:
|
||||
submarineSelection?.Update();
|
||||
break;
|
||||
|
||||
case CampaignMode.InteractionType.Crew:
|
||||
CrewManagement?.Update();
|
||||
break;
|
||||
|
||||
case CampaignMode.InteractionType.Store:
|
||||
Store?.Update(deltaTime);
|
||||
break;
|
||||
|
||||
break;
|
||||
case CampaignMode.InteractionType.MedicalClinic:
|
||||
MedicalClinic?.Update(deltaTime);
|
||||
break;
|
||||
case CampaignMode.InteractionType.Map:
|
||||
if (StartButton != null)
|
||||
{
|
||||
StartButton.Enabled = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap) && Character.Controlled is { IsIncapacitated: false };
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,7 +571,6 @@ namespace Barotrauma
|
||||
StartButton.Visible = false;
|
||||
missionList.Enabled = false;
|
||||
}
|
||||
//locationInfoPanel?.UpdateAuto(1.0f);
|
||||
}
|
||||
|
||||
public void SelectTab(CampaignMode.InteractionType tab, Character npc = null)
|
||||
|
||||
@@ -435,8 +435,11 @@ namespace Barotrauma
|
||||
|
||||
graphics.BlendState = BlendState.NonPremultiplied;
|
||||
graphics.SamplerStates[0] = SamplerState.PointClamp;
|
||||
graphics.SamplerStates[1] = SamplerState.PointClamp;
|
||||
GameMain.LightManager.LosEffect.CurrentTechnique.Passes[0].Apply();
|
||||
Quad.Render();
|
||||
graphics.SamplerStates[0] = SamplerState.LinearWrap;
|
||||
graphics.SamplerStates[1] = SamplerState.LinearWrap;
|
||||
}
|
||||
|
||||
if (Character.Controlled is { } character)
|
||||
|
||||
@@ -59,7 +59,6 @@ namespace Barotrauma
|
||||
private GUIImage playstyleBanner;
|
||||
private GUITextBlock playstyleDescription;
|
||||
|
||||
private const string RemoteContentUrl = "http://www.barotraumagame.com/gamedata/";
|
||||
private readonly GUIComponent remoteContentContainer;
|
||||
private XDocument remoteContentDoc;
|
||||
|
||||
@@ -1525,10 +1524,11 @@ namespace Barotrauma
|
||||
|
||||
private void FetchRemoteContent()
|
||||
{
|
||||
if (string.IsNullOrEmpty(RemoteContentUrl)) { return; }
|
||||
string remoteContentUrl = GameSettings.CurrentConfig.RemoteMainMenuContentUrl;
|
||||
if (string.IsNullOrEmpty(remoteContentUrl)) { return; }
|
||||
try
|
||||
{
|
||||
var client = new RestClient(RemoteContentUrl);
|
||||
var client = new RestClient(remoteContentUrl);
|
||||
var request = new RestRequest("MenuContent.xml", Method.GET);
|
||||
TaskPool.Add("RequestMainMenuRemoteContent", client.ExecuteAsync(request),
|
||||
RemoteContentReceived);
|
||||
|
||||
@@ -751,12 +751,20 @@ namespace Barotrauma
|
||||
};
|
||||
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);
|
||||
RefreshServerInfoSize();
|
||||
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) =>
|
||||
{
|
||||
string str = textBox.Text;
|
||||
|
||||
+54
-15
@@ -812,7 +812,7 @@ namespace Barotrauma
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
@@ -848,8 +848,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (c1.GUIComponent.UserData is not ServerInfo s1) { return 0; }
|
||||
if (c2.GUIComponent.UserData is not ServerInfo s2) { return 0; }
|
||||
int comparison = sortedAscending ? 1 : -1;
|
||||
return CompareServer(sortBy, s1, s2) * comparison;
|
||||
return CompareServer(sortBy, s1, s2, sortedAscending);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -857,22 +856,31 @@ namespace Barotrauma
|
||||
{
|
||||
var children = serverList.Content.RectTransform.Children.Reverse().ToList();
|
||||
|
||||
int comparison = sortedAscending ? 1 : -1;
|
||||
foreach (var child in children)
|
||||
{
|
||||
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);
|
||||
component.RectTransform.RepositionChildInHierarchy(index + 1);
|
||||
component.RectTransform.RepositionChildInHierarchy(Math.Min(index + 1, serverList.Content.CountChildren - 1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
case ColumnLabel.ServerListCompatible:
|
||||
@@ -880,18 +888,18 @@ namespace Barotrauma
|
||||
bool s2Compatible = NetworkMember.IsCompatible(GameMain.Version, s2.GameVersion);
|
||||
|
||||
if (s1Compatible == s2Compatible) { return 0; }
|
||||
return s1Compatible ? -1 : 1;
|
||||
return (s1Compatible ? -1 : 1) * comparison;
|
||||
case ColumnLabel.ServerListHasPassword:
|
||||
if (s1.HasPassword == s2.HasPassword) { return 0; }
|
||||
return s1.HasPassword ? 1 : -1;
|
||||
return (s1.HasPassword ? 1 : -1) * comparison;
|
||||
case ColumnLabel.ServerListName:
|
||||
// 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:
|
||||
if (s1.GameStarted == s2.GameStarted) { return 0; }
|
||||
return s1.GameStarted ? 1 : -1;
|
||||
return (s1.GameStarted ? 1 : -1) * comparison;
|
||||
case ColumnLabel.ServerListPlayers:
|
||||
return s2.PlayerCount.CompareTo(s1.PlayerCount);
|
||||
return s2.PlayerCount.CompareTo(s1.PlayerCount) * comparison;
|
||||
case ColumnLabel.ServerListPing:
|
||||
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),
|
||||
(false, true) => 1,
|
||||
(true, false) => -1
|
||||
};
|
||||
} * comparison;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -1504,9 +1512,41 @@ namespace Barotrauma
|
||||
|
||||
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 < 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.NoServers);
|
||||
@@ -1522,7 +1562,6 @@ namespace Barotrauma
|
||||
UpdateServerInfoUI(serverInfo);
|
||||
if (!skipPing) { PingUtils.GetServerPing(serverInfo, UpdateServerInfoUI); }
|
||||
|
||||
InsertServer(serverInfo, serverFrame);
|
||||
}
|
||||
|
||||
private void UpdateServerInfoUI(ServerInfo serverInfo)
|
||||
@@ -1736,7 +1775,7 @@ namespace Barotrauma
|
||||
AddToFavoriteServers(serverInfo);
|
||||
}
|
||||
|
||||
SortList(sortedBy, toggle: false);
|
||||
InsertServer(serverInfo, serverFrame);
|
||||
FilterServers();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Steam;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Steam;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -525,6 +525,11 @@ namespace Barotrauma
|
||||
GUI.AddMessage(TextManager.Get("waypointsgeneratedsuccesfully"), GUIStyle.Green);
|
||||
}
|
||||
WayPoint.ShowWayPoints = true;
|
||||
var matchingTickBox = showEntitiesTickBoxes?.Find(tb => tb.UserData as string == "waypoint");
|
||||
if (matchingTickBox != null)
|
||||
{
|
||||
matchingTickBox.Selected = true;
|
||||
}
|
||||
generateWaypointsVerification.Close();
|
||||
return true;
|
||||
};
|
||||
@@ -2847,7 +2852,7 @@ namespace Barotrauma
|
||||
{
|
||||
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)
|
||||
.Distinct().OfType<ContentPackage>().Select(p => p.Name).ToHashSet();
|
||||
var tickboxes = requiredContentPackList.Content.Children.OfType<GUITickBox>().ToArray();
|
||||
@@ -3546,10 +3551,46 @@ namespace Barotrauma
|
||||
TextManager.Get("LoadingVanillaSubmarineHeader"),
|
||||
TextManager.Get("LoadingVanillaSubmarineDesc"));
|
||||
|
||||
public void LoadSub(SubmarineInfo info)
|
||||
public void LoadSub(SubmarineInfo info, bool checkIdConflicts = true)
|
||||
{
|
||||
Submarine.Unload();
|
||||
Submarine selectedSub = null;
|
||||
|
||||
if (checkIdConflicts)
|
||||
{
|
||||
Dictionary<int, Identifier> entities = new Dictionary<int, Identifier>();
|
||||
foreach (var subElement in info.SubmarineElement.Elements())
|
||||
{
|
||||
int id = subElement.GetAttributeInt("ID", -1);
|
||||
if (id == -1) { continue; }
|
||||
Identifier identifier = subElement.GetAttributeIdentifier("identifier", string.Empty);
|
||||
if (entities.TryGetValue(id, out Identifier duplicateEntity))
|
||||
{
|
||||
var errorMsg = new GUIMessageBox(
|
||||
TextManager.Get("error"),
|
||||
TextManager.GetWithVariables("subeditor.duplicateiderror",
|
||||
("[entity1]", $"{duplicateEntity} ({id})"),
|
||||
("[entity2]", $"{identifier} ({id})")),
|
||||
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
|
||||
errorMsg.Buttons[0].OnClicked = (bnt, userdata) =>
|
||||
{
|
||||
subElement.Remove();
|
||||
LoadSub(info, checkIdConflicts: false);
|
||||
errorMsg.Close();
|
||||
return true;
|
||||
};
|
||||
errorMsg.Buttons[1].OnClicked = (bnt, userdata) =>
|
||||
{
|
||||
LoadSub(info, checkIdConflicts: false);
|
||||
errorMsg.Close();
|
||||
return true;
|
||||
};
|
||||
return;
|
||||
}
|
||||
entities.Add(id, identifier);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
selectedSub = new Submarine(info);
|
||||
@@ -5755,7 +5796,10 @@ namespace Barotrauma
|
||||
{
|
||||
item.SetTransform(dummyCharacter.SimPosition, 0.0f);
|
||||
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
|
||||
Wire wire = item.GetComponent<Wire>();
|
||||
@@ -5868,6 +5912,11 @@ namespace Barotrauma
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
if (GameMain.LightManager.DebugLos)
|
||||
{
|
||||
GameMain.LightManager.DebugDrawLos(spriteBatch, cam);
|
||||
}
|
||||
|
||||
//-------------------- HUD -----------------------------
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState);
|
||||
|
||||
@@ -343,12 +343,11 @@ namespace Barotrauma
|
||||
if (property.PropertyType == typeof(string) && value == null)
|
||||
{
|
||||
value = "";
|
||||
}
|
||||
}
|
||||
|
||||
Identifier propertyTag = $"{property.PropertyInfo.DeclaringType.Name}.{property.PropertyInfo.Name}".ToIdentifier();
|
||||
Identifier fallbackTag = property.PropertyInfo.Name.ToIdentifier();
|
||||
LocalizedString displayName =
|
||||
TextManager.Get(propertyTag, $"sp.{propertyTag}.name".ToIdentifier());
|
||||
LocalizedString displayName = TextManager.Get(propertyTag, $"sp.{propertyTag}.name".ToIdentifier());
|
||||
if (displayName.IsNullOrEmpty())
|
||||
{
|
||||
Editable editable = property.GetAttribute<Editable>();
|
||||
@@ -380,10 +379,14 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
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();
|
||||
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())
|
||||
{
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace Barotrauma.Steam
|
||||
CrossThread.RequestExecutionOnMainThread(() => ContentPackageManager.LocalPackages.Refresh());
|
||||
}
|
||||
|
||||
public static async Task CreatePublishStagingCopy(string modVersion, ContentPackage contentPackage)
|
||||
public static async Task CreatePublishStagingCopy(string title, string modVersion, ContentPackage contentPackage)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
@@ -184,11 +184,13 @@ namespace Barotrauma.Steam
|
||||
throw new Exception("Staging copy could not be loaded",
|
||||
result.TryUnwrapFailure(out var exception) ? exception : null);
|
||||
}
|
||||
|
||||
//Load filelist.xml and write the hash into it so anyone downloading this mod knows what it should be
|
||||
|
||||
//Load filelist.xml and write the hash into it so anyone downloading this mod knows what it should be
|
||||
ModProject modProject = new ModProject(tempPkg)
|
||||
{
|
||||
ModVersion = modVersion
|
||||
ModVersion = modVersion,
|
||||
Name = title,
|
||||
ExpectedHash = tempPkg.CalculateHash(name: title, modVersion: modVersion)
|
||||
};
|
||||
modProject.Save(stagingFileListPath);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Steamworks;
|
||||
using Directory = Barotrauma.IO.Directory;
|
||||
using ItemOrPackage = Barotrauma.Either<Steamworks.Ugc.Item, Barotrauma.ContentPackage>;
|
||||
using Path = Barotrauma.IO.Path;
|
||||
@@ -157,7 +158,7 @@ namespace Barotrauma.Steam
|
||||
}
|
||||
|
||||
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);
|
||||
if (workshopItem.Id != 0)
|
||||
{
|
||||
@@ -212,7 +213,7 @@ namespace Barotrauma.Steam
|
||||
};
|
||||
|
||||
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);
|
||||
var descriptionTextBox
|
||||
@@ -320,7 +321,9 @@ namespace Barotrauma.Steam
|
||||
workshopItem.Id == 0
|
||||
? Steamworks.Ugc.Editor.NewCommunityFile
|
||||
: new Steamworks.Ugc.Editor(workshopItem.Id);
|
||||
ugcEditor = ugcEditor.WithTitle(titleTextBox.Text)
|
||||
ugcEditor = ugcEditor
|
||||
.InLanguage(SteamUtils.SteamUILanguage ?? string.Empty)
|
||||
.WithTitle(titleTextBox.Text)
|
||||
.WithDescription(descriptionTextBox.Text)
|
||||
.WithTags(tagButtons.Where(kvp => kvp.Value.Selected).Select(kvp => kvp.Key.Value))
|
||||
.WithChangeLog(changeNoteTextBox.Text)
|
||||
@@ -478,7 +481,7 @@ namespace Barotrauma.Steam
|
||||
bool stagingReady = false;
|
||||
Exception? stagingException = null;
|
||||
TaskPool.Add("CreatePublishStagingCopy",
|
||||
SteamManager.Workshop.CreatePublishStagingCopy(modVersion, localPackage),
|
||||
SteamManager.Workshop.CreatePublishStagingCopy(editor.Title ?? localPackage.Name, modVersion, localPackage),
|
||||
(t) =>
|
||||
{
|
||||
stagingReady = true;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal class LeakyBucket
|
||||
{
|
||||
private readonly Queue<Action> queue;
|
||||
private readonly int capacity;
|
||||
private readonly float cooldownInSeconds;
|
||||
private float timer;
|
||||
|
||||
public LeakyBucket(float cooldownInSeconds, int capacity)
|
||||
{
|
||||
this.cooldownInSeconds = cooldownInSeconds;
|
||||
this.capacity = capacity;
|
||||
queue = new Queue<Action>(capacity);
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (timer > 0f)
|
||||
{
|
||||
timer -= deltaTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (queue.Count is 0) { return; }
|
||||
|
||||
TryDequeue();
|
||||
}
|
||||
|
||||
private void TryDequeue()
|
||||
{
|
||||
timer = cooldownInSeconds;
|
||||
if (queue.TryDequeue(out var action))
|
||||
{
|
||||
action.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryEnqueue(Action item)
|
||||
{
|
||||
if (queue.Count >= capacity) { return false; }
|
||||
queue.Enqueue(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user