Unstable 0.15.15.0 (and the one before it I forgor)

This commit is contained in:
Markus Isberg
2021-11-18 21:34:30 +09:00
parent 10e5fd5f3e
commit 80f39cd2a3
257 changed files with 4916 additions and 2582 deletions
@@ -17,7 +17,7 @@ namespace Barotrauma
fadeOutRoutine = CoroutineManager.StartCoroutine(FadeOutColors(Config.DeadEntityColorFadeOutTime)); fadeOutRoutine = CoroutineManager.StartCoroutine(FadeOutColors(Config.DeadEntityColorFadeOutTime));
} }
private IEnumerable<object> FadeOutColors(float time) private IEnumerable<CoroutineStatus> FadeOutColors(float time)
{ {
float timer = 0; float timer = 0;
while (timer < time) while (timer < time)
@@ -36,7 +36,7 @@ namespace Barotrauma
CharacterStateInfo serverPos = character.MemState.Last(); CharacterStateInfo serverPos = character.MemState.Last();
if (!character.isSynced) if (!character.isSynced)
{ {
SetPosition(serverPos.Position, false); SetPosition(serverPos.Position, lerp: false);
Collider.LinearVelocity = Vector2.Zero; Collider.LinearVelocity = Vector2.Zero;
character.MemLocalState.Clear(); character.MemLocalState.Clear();
character.LastNetworkUpdateID = serverPos.ID; character.LastNetworkUpdateID = serverPos.ID;
@@ -159,7 +159,7 @@ namespace Barotrauma
if (!character.isSynced) if (!character.isSynced)
{ {
SetPosition(serverPos.Position, false); SetPosition(serverPos.Position, lerp: false);
Collider.LinearVelocity = Vector2.Zero; Collider.LinearVelocity = Vector2.Zero;
character.MemLocalState.Clear(); character.MemLocalState.Clear();
character.LastNetworkUpdateID = serverPos.ID; character.LastNetworkUpdateID = serverPos.ID;
@@ -581,10 +581,10 @@ namespace Barotrauma
if (this is HumanoidAnimController humanoid) if (this is HumanoidAnimController humanoid)
{ {
Vector2 pos = ConvertUnits.ToDisplayUnits(humanoid.RightHandIKPos); Vector2 pos = ConvertUnits.ToDisplayUnits(humanoid.RightHandIKPos);
if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.Position; } if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.DrawPosition; }
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true); GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
pos = ConvertUnits.ToDisplayUnits(humanoid.LeftHandIKPos); pos = ConvertUnits.ToDisplayUnits(humanoid.LeftHandIKPos);
if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.Position; } if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.DrawPosition; }
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true); GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
Vector2 aimPos = humanoid.AimSourceWorldPos; Vector2 aimPos = humanoid.AimSourceWorldPos;
@@ -447,28 +447,7 @@ namespace Barotrauma
if (GameMain.Client != null) { chatMessage += " " + TextManager.Get("DeathChatNotification"); } if (GameMain.Client != null) { chatMessage += " " + TextManager.Get("DeathChatNotification"); }
if (GameMain.NetworkMember.RespawnManager?.UseRespawnPrompt ?? false) GameMain.NetworkMember.RespawnManager?.ShowRespawnPromptIfNeeded();
{
CoroutineManager.Invoke(() =>
{
if (controlled != null || (!(GameMain.GameSession?.IsRunning ?? false))) { return; }
var respawnPrompt = new GUIMessageBox(
TextManager.Get("tutorial.tryagainheader"), TextManager.Get("respawnquestionprompt"),
new string[] { TextManager.Get("respawnquestionpromptrespawn"), TextManager.Get("respawnquestionpromptwait") });
respawnPrompt.Buttons[0].OnClicked += (btn, userdata) =>
{
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: false);
respawnPrompt.Close();
return true;
};
respawnPrompt.Buttons[1].OnClicked += (btn, userdata) =>
{
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: true);
respawnPrompt.Close();
return true;
};
}, delay: 5.0f);
}
GameMain.NetworkMember.AddChatMessage(chatMessage, ChatMessageType.Dead); GameMain.NetworkMember.AddChatMessage(chatMessage, ChatMessageType.Dead);
GameMain.LightManager.LosEnabled = false; GameMain.LightManager.LosEnabled = false;
@@ -235,7 +235,7 @@ namespace Barotrauma
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (item.Submarine == null || item.Submarine.TeamID != character.TeamID || item.Submarine.Info.IsWreck) { continue; } if (item.Submarine == null || item.Submarine.TeamID != character.TeamID || item.Submarine.Info.IsWreck) { continue; }
if (!item.Repairables.Any(r => item.ConditionPercentage <= r.RepairIconThreshold)) { continue; } if (!item.Repairables.Any(r => r.IsBelowRepairIconThreshold)) { continue; }
if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; } if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; }
Vector2 diff = item.WorldPosition - character.WorldPosition; Vector2 diff = item.WorldPosition - character.WorldPosition;
@@ -790,10 +790,7 @@ namespace Barotrauma
return false; return false;
} }
}; };
//force update twice because the listbox is insanely janky listBox.ForceLayoutRecalculation();
//TODO: fix all of the UI :)
listBox.ForceUpdate();
listBox.ForceUpdate();
foreach (var childLayoutGroup in listBox.Content.GetAllChildren<GUILayoutGroup>()) foreach (var childLayoutGroup in listBox.Content.GetAllChildren<GUILayoutGroup>())
{ {
childLayoutGroup.Recalculate(); childLayoutGroup.Recalculate();
@@ -371,21 +371,22 @@ namespace Barotrauma
if (attackLimbIndex == 255 || Removed) { break; } if (attackLimbIndex == 255 || Removed) { break; }
if (attackLimbIndex >= AnimController.Limbs.Length) if (attackLimbIndex >= AnimController.Limbs.Length)
{ {
DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})"); DebugConsole.ThrowError($"Received invalid {(eventType == 4 ? "SetAttackTarget" : "ExecuteAttack")} message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
break; break;
} }
Limb attackLimb = AnimController.Limbs[attackLimbIndex]; Limb attackLimb = AnimController.Limbs[attackLimbIndex];
Limb targetLimb = null; Limb targetLimb = null;
if (!(FindEntityByID(targetEntityID) is IDamageable targetEntity)) IDamageable targetEntity = FindEntityByID(targetEntityID) as IDamageable;
if (targetEntity == null && eventType == 4)
{ {
DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Target entity not found (ID {targetEntityID})"); DebugConsole.ThrowError($"Received invalid SetAttackTarget message. Target entity not found (ID {targetEntityID})");
break; break;
} }
if (targetEntity is Character targetCharacter) if (targetEntity is Character targetCharacter)
{ {
if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length) if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
{ {
DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})"); DebugConsole.ThrowError($"Received invalid {(eventType == 4 ? "SetAttackTarget" : "ExecuteAttack")} message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})");
break; break;
} }
targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex]; targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
@@ -805,7 +805,27 @@ namespace Barotrauma
{ {
var treatmentButton = component.GetChild<GUIButton>(); var treatmentButton = component.GetChild<GUIButton>();
if (!(treatmentButton?.UserData is ItemPrefab itemPrefab)) { continue; } if (!(treatmentButton?.UserData is ItemPrefab itemPrefab)) { continue; }
treatmentButton.Enabled = Character.Controlled.Inventory.AllItems.Any(it => it.prefab == itemPrefab); var matchingItem = Character.Controlled.Inventory.FindItem(it => it.prefab == itemPrefab, recursive: true);
treatmentButton.Enabled = matchingItem != null;
if (treatmentButton.Enabled && treatmentButton.State == GUIComponent.ComponentState.Hover)
{
//highlight the slot the treatment item is in
var rootContainer = matchingItem.GetRootContainer() ?? 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)
{
Character.Controlled.Inventory.visualSlots[index].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f);
}
}
if (matchingItem != null && !string.IsNullOrEmpty(treatmentButton.ToolTip)) { continue; }
treatmentButton.ToolTip = $"‖color:255,255,255,255‖{itemPrefab.Name}‖color:end‖" + '\n' + itemPrefab.Description;
if (treatmentButton.Enabled)
{
treatmentButton.ToolTip =
$"‖color:gui.green‖[{TextManager.Get(PlayerInput.MouseButtonsSwapped() ? "input.rightmouse" : "input.leftmouse")}] {TextManager.Get("quickuseaction.usetreatment")}‖color:end‖" + '\n'
+ treatmentButton.RawToolTip;
}
foreach (GUIComponent child in treatmentButton.Children) foreach (GUIComponent child in treatmentButton.Children)
{ {
child.Enabled = treatmentButton.Enabled; child.Enabled = treatmentButton.Enabled;
@@ -1249,7 +1269,7 @@ namespace Barotrauma
foreach (string treatment in treatmentSuitability.Keys.ToList()) foreach (string treatment in treatmentSuitability.Keys.ToList())
{ {
//prefer suggestions for items the player has //prefer suggestions for items the player has
if (Character.Controlled.Inventory.FindItemByIdentifier(treatment) != null) if (Character.Controlled.Inventory.FindItemByIdentifier(treatment, recursive: true) != null)
{ {
treatmentSuitability[treatment] *= 10.0f; treatmentSuitability[treatment] *= 10.0f;
} }
@@ -1288,12 +1308,11 @@ namespace Barotrauma
var innerFrame = new GUIButton(new RectTransform(Vector2.One, itemSlot.RectTransform, Anchor.Center, Pivot.Center, scaleBasis: ScaleBasis.Smallest), style: "SubtreeHeader") var innerFrame = new GUIButton(new RectTransform(Vector2.One, itemSlot.RectTransform, Anchor.Center, Pivot.Center, scaleBasis: ScaleBasis.Smallest), style: "SubtreeHeader")
{ {
UserData = item, UserData = item,
ToolTip = $"‖color:255,255,255,255‖{item.Name}‖color:end‖" + '\n' + item.Description,
DisabledColor = Color.White * 0.1f, DisabledColor = Color.White * 0.1f,
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
if (!(userdata is ItemPrefab itemPrefab)) { return false; } if (!(userdata is ItemPrefab itemPrefab)) { return false; }
var item = Character.Controlled.Inventory.AllItems.FirstOrDefault(it => it.prefab == itemPrefab); var item = Character.Controlled.Inventory.FindItem(it => it.prefab == itemPrefab, recursive: true);
if (item == null) { return false; } if (item == null) { return false; }
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex); Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex);
item.ApplyTreatment(Character.Controlled, Character, targetLimb); item.ApplyTreatment(Character.Controlled, Character, targetLimb);
@@ -1445,7 +1464,7 @@ namespace Barotrauma
var potentialTreatment = Inventory.DraggingItems.FirstOrDefault(); var potentialTreatment = Inventory.DraggingItems.FirstOrDefault();
if (potentialTreatment == null && GUI.MouseOn?.UserData is ItemPrefab itemPrefab) if (potentialTreatment == null && GUI.MouseOn?.UserData is ItemPrefab itemPrefab)
{ {
potentialTreatment = Character.Controlled.Inventory.AllItems.FirstOrDefault(it => it.prefab == itemPrefab); potentialTreatment = Character.Controlled.Inventory.FindItem(it => it.prefab == itemPrefab, recursive: true);
} }
potentialTreatment ??= Inventory.SelectedSlot?.Item; potentialTreatment ??= Inventory.SelectedSlot?.Item;
@@ -30,7 +30,7 @@ namespace Barotrauma
foreach (SkillPrefab skill in Skills) foreach (SkillPrefab skill in Skills)
{ {
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillContainer.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillContainer.RectTransform),
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + skill.Identifier), (int)skill.LevelRange.X + " - " + (int)skill.LevelRange.Y), " - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + skill.Identifier), (int)skill.LevelRange.Start + " - " + (int)skill.LevelRange.End),
font: GUI.SmallFont); font: GUI.SmallFont);
} }
@@ -730,8 +730,6 @@ namespace Barotrauma
} }
} }
body.Dir = Dir;
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes"); float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
bool hideLimb = Hide || bool hideLimb = Hide ||
@@ -138,7 +138,7 @@ namespace Barotrauma
var newMsg = queuedMessages.Dequeue(); var newMsg = queuedMessages.Dequeue();
AddMessage(newMsg); AddMessage(newMsg);
if (GameSettings.SaveDebugConsoleLogs) if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging)
{ {
unsavedMessages.Add(newMsg); unsavedMessages.Add(newMsg);
if (unsavedMessages.Count >= messagesPerFile) if (unsavedMessages.Count >= messagesPerFile)
@@ -274,7 +274,10 @@ namespace Barotrauma
AddMessage(newMsg); AddMessage(newMsg);
} }
if (GameSettings.SaveDebugConsoleLogs) unsavedMessages.Add(newMsg); if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging)
{
unsavedMessages.Add(newMsg);
}
} }
} }
} }
@@ -537,7 +540,21 @@ namespace Barotrauma
return; return;
} }
GameMain.MainMenuScreen.QuickStart(fixedSeed: false, subName); float difficulty = 40;
if (args.Length > 1)
{
float.TryParse(args[1], out difficulty);
}
LevelGenerationParams levelGenerationParams = null;
if (args.Length > 2)
{
string levelGenerationIdentifier = args[2];
levelGenerationParams = LevelGenerationParams.LevelParams.FirstOrDefault(p => p.Identifier == levelGenerationIdentifier);
}
GameMain.MainMenuScreen.QuickStart(fixedSeed: false, subName, difficulty, levelGenerationParams);
}, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().ToArray() })); }, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().ToArray() }));
commands.Add(new Command("steamnetdebug", "steamnetdebug: Toggles Steamworks networking debug logging.", (string[] args) => commands.Add(new Command("steamnetdebug", "steamnetdebug: Toggles Steamworks networking debug logging.", (string[] args) =>
@@ -734,13 +751,10 @@ namespace Barotrauma
AssignOnExecute("teleportcharacter|teleport", (string[] args) => AssignOnExecute("teleportcharacter|teleport", (string[] args) =>
{ {
Character tpCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args, false); Character tpCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args, false);
if (tpCharacter == null) return; if (tpCharacter != null)
{
var cam = GameMain.GameScreen.Cam; tpCharacter.TeleportTo(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition));
tpCharacter.AnimController.CurrentHull = null; }
tpCharacter.Submarine = null;
tpCharacter.AnimController.SetPosition(ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition)));
tpCharacter.AnimController.FindHull(cam.ScreenToWorld(PlayerInput.MousePosition), true);
}); });
AssignOnExecute("spawn|spawncharacter", (string[] args) => AssignOnExecute("spawn|spawncharacter", (string[] args) =>
@@ -1413,7 +1427,7 @@ namespace Barotrauma
commands.Add(new Command("analyzeitem", "analyzeitem: Analyzes one item for exploits.", (string[] args) => commands.Add(new Command("analyzeitem", "analyzeitem: Analyzes one item for exploits.", (string[] args) =>
{ {
if (args.Length < 1) return; if (args.Length < 1) { return; }
List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>(); List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>();
foreach (ItemPrefab iPrefab in ItemPrefab.Prefabs) foreach (ItemPrefab iPrefab in ItemPrefab.Prefabs)
@@ -1792,7 +1806,7 @@ namespace Barotrauma
foreach (var talentTree in TalentTree.JobTalentTrees) foreach (var talentTree in TalentTree.JobTalentTrees)
{ {
foreach (var talentSubTree in talentTree.Value.TalentSubTrees) foreach (var talentSubTree in talentTree.TalentSubTrees)
{ {
string nameIdentifier = "talenttree." + talentSubTree.Identifier; string nameIdentifier = "talenttree." + talentSubTree.Identifier;
if (!tags[language].Contains(nameIdentifier)) if (!tags[language].Contains(nameIdentifier))
@@ -1857,7 +1871,21 @@ namespace Barotrauma
commands.Add(new Command("eventstats", "", (string[] args) => commands.Add(new Command("eventstats", "", (string[] args) =>
{ {
var debugLines = EventSet.GetDebugStatistics(); List<string> debugLines;
if (args.Length > 0)
{
if (!Enum.TryParse(args[0], ignoreCase: true, out Level.PositionType spawnType))
{
var enums = Enum.GetNames(typeof(Level.PositionType));
ThrowError($"\"{args[0]}\" is not a valid Level.PositionType. Available options are: {string.Join(", ", enums)}");
return;
}
debugLines = EventSet.GetDebugStatistics(filter: monsterEvent => monsterEvent.SpawnPosType.HasFlag(spawnType));
}
else
{
debugLines = EventSet.GetDebugStatistics();
}
string filePath = "eventstats.txt"; string filePath = "eventstats.txt";
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(filePath, debugLines); File.WriteAllLines(filePath, debugLines);
@@ -14,7 +14,8 @@ namespace Barotrauma
{ {
private Graph intensityGraph; private Graph intensityGraph;
private Graph targetIntensityGraph; private Graph targetIntensityGraph;
private float intensityGraphUpdateInterval; private Graph monsterStrengthGraph;
private const float intensityGraphUpdateInterval = 10;
private float lastIntensityUpdate; private float lastIntensityUpdate;
private Vector2 pinnedPosition = new Vector2(256, 128); private Vector2 pinnedPosition = new Vector2(256, 128);
@@ -22,6 +23,8 @@ namespace Barotrauma
public Event? PinnedEvent { get; set; } public Event? PinnedEvent { get; set; }
private bool isGraphSelected;
public void DebugDraw(SpriteBatch spriteBatch) public void DebugDraw(SpriteBatch spriteBatch)
{ {
foreach (Event ev in activeEvents) foreach (Event ev in activeEvents)
@@ -42,17 +45,25 @@ namespace Barotrauma
DrawEventTargetTags(spriteBatch, scriptedEvent); DrawEventTargetTags(spriteBatch, scriptedEvent);
} }
float theoreticalMaxMonsterStrength = 10000;
float relativeMaxMonsterStrength = theoreticalMaxMonsterStrength * GameMain.GameSession.LevelData.Difficulty / 100;
float absoluteMonsterStrength = monsterStrength / theoreticalMaxMonsterStrength;
float relativeMonsterStrength = monsterStrength / relativeMaxMonsterStrength;
GUI.DrawString(spriteBatch, new Vector2(10, y), "EventManager", Color.White, Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(10, y), "EventManager", Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 20), "Event cooldown: " + (int)Math.Max(eventCoolDown, 0), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 20), "Event cooldown: " + (int)Math.Max(eventCoolDown, 0), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 35), "Current intensity: " + (int)Math.Round(currentIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, currentIntensity), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 35), "Current intensity: " + (int)Math.Round(currentIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, currentIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 50), "Target intensity: " + (int)Math.Round(targetIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, targetIntensity), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 50), "Target intensity: " + (int)Math.Round(targetIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, targetIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 65), "AvgHealth: " + (int)Math.Round(avgCrewHealth * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgCrewHealth), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 65), "Crew health: " + (int)Math.Round(avgCrewHealth * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgCrewHealth), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 80), "AvgHullIntegrity: " + (int)Math.Round(avgHullIntegrity * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgHullIntegrity), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 80), "Hull integrity: " + (int)Math.Round(avgHullIntegrity * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgHullIntegrity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 95), "FloodingAmount: " + (int)Math.Round(floodingAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, floodingAmount), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 95), "Flooding amount: " + (int)Math.Round(floodingAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, floodingAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 110), "FireAmount: " + (int)Math.Round(fireAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, fireAmount), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 110), "Fire amount: " + (int)Math.Round(fireAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, fireAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 125), "EnemyDanger: " + (int)Math.Round(enemyDanger * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, enemyDanger), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 125), "Enemy danger: " + (int)Math.Round(enemyDanger * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, enemyDanger), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 140), "MonsterTotalStrength: " + (int)Math.Round(monsterTotalStrength), Color.Lerp(GUI.Style.Green, GUI.Style.Red, monsterTotalStrength / 5000f), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 140), "Current monster strength (total): " + (int)Math.Round(monsterStrength), Color.Lerp(GUI.Style.Green, GUI.Style.Red, relativeMonsterStrength), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 155), "Main events: " + (int)Math.Round(CumulativeMonsterStrengthMain), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 170), "Ruin events: " + (int)Math.Round(CumulativeMonsterStrengthRuins), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 185), "Wreck events: " + (int)Math.Round(CumulativeMonsterStrengthWrecks), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 200), "Cave events: " + (int)Math.Round(CumulativeMonsterStrengthCaves), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
#if DEBUG #if DEBUG
if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt) && if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt) &&
@@ -64,29 +75,103 @@ namespace Barotrauma
if (intensityGraph == null) if (intensityGraph == null)
{ {
intensityGraph = new Graph(); int graphDensity = 360; // 60 min
targetIntensityGraph = new Graph(); intensityGraph = new Graph(graphDensity);
targetIntensityGraph = new Graph(graphDensity);
monsterStrengthGraph = new Graph(graphDensity);
} }
intensityGraphUpdateInterval = 5.0f;
if (Timing.TotalTime > lastIntensityUpdate + intensityGraphUpdateInterval) if (Timing.TotalTime > lastIntensityUpdate + intensityGraphUpdateInterval)
{ {
intensityGraph.Update(currentIntensity); intensityGraph.Update(currentIntensity);
targetIntensityGraph.Update(targetIntensity); targetIntensityGraph.Update(targetIntensity);
lastIntensityUpdate = (float) Timing.TotalTime; monsterStrengthGraph.Update(relativeMonsterStrength);
lastIntensityUpdate = (float)Timing.TotalTime;
} }
Rectangle graphRect = new Rectangle(15, y + 165, 150, 50); Rectangle graphRect = new Rectangle(15, y + 240, (int)(200 * GUI.xScale), (int)(100 * GUI.yScale));
bool isGraphHovered = graphRect.Contains(PlayerInput.MousePosition);
bool leftMousePressed = PlayerInput.PrimaryMouseButtonDown() || PlayerInput.PrimaryMouseButtonHeld();
bool rightMousePressed = PlayerInput.SecondaryMouseButtonHeld() || PlayerInput.SecondaryMouseButtonDown();
if (!isGraphSelected && isGraphHovered && leftMousePressed)
{
isGraphSelected = true;
}
if (isGraphSelected && rightMousePressed)
{
isGraphSelected = false;
}
Color intensityColor = Color.Lerp(Color.White, GUI.Style.Red, currentIntensity);
if (isGraphHovered || isGraphSelected)
{
graphRect.Size = new Point(GameMain.GraphicsWidth - 30, (int)(GameMain.GraphicsHeight * 0.35f));
intensityColor = Color.Red;
GUI.DrawRectangle(spriteBatch, graphRect, Color.Black * 0.95f, isFilled: true);
}
else
{
GUI.DrawRectangle(spriteBatch, graphRect, Color.Black * 0.6f, isFilled: true);
}
intensityGraph.Draw(spriteBatch, graphRect, maxValue: 1.0f, xOffset: 0, intensityColor, (sBatch, value, order, pos) =>
{
if (isGraphHovered || isGraphSelected)
{
Vector2 bottomPoint = new Vector2(pos.X, graphRect.Bottom);
float height = 3 * GUI.yScale;
if (order % 6 == 0)
{
height *= 3;
string text = (order / 6).ToString();
var font = GUI.SmallFont;
Vector2 textSize = font.MeasureString(text);
Vector2 textPos = new Vector2(bottomPoint.X - textSize.X / 2, bottomPoint.Y + height * 1.5f);
GUI.DrawString(sBatch, textPos, text, Color.White, font: font);
}
GUI.DrawLine(sBatch, bottomPoint, bottomPoint + Vector2.UnitY * height, Color.White, width: Math.Max(GUI.Scale, 1));
DrawTimeStamps(sBatch, Color.Red, pos, order);
}
});
targetIntensityGraph.Draw(spriteBatch, graphRect, maxValue: 1.0f, xOffset: 0, intensityColor * 0.5f);
if (isGraphHovered || isGraphSelected)
{
float? maxValue = 1;
Color color = Color.White;
if (relativeMonsterStrength > 1)
{
maxValue = null;
color = Color.Yellow;
}
monsterStrengthGraph.Draw(spriteBatch, graphRect, maxValue, color: color, doForEachValue: (sBatch, value, order, pos) => DrawTimeStamps(sBatch, color, pos, order));
}
GUI.DrawRectangle(spriteBatch, graphRect, Color.Black * 0.5f, true); void DrawTimeStamps(SpriteBatch sBatch, Color color, Vector2 pos, int order)
intensityGraph.Draw(spriteBatch, graphRect, 1.0f, 0.0f, Color.Lerp(Color.White, GUI.Style.Red, currentIntensity)); {
targetIntensityGraph.Draw(spriteBatch, graphRect, 1.0f, 0.0f, Color.Lerp(Color.White, GUI.Style.Red, targetIntensity) * 0.5f); if (isGraphHovered || isGraphSelected)
{
foreach (var timeStamp in timeStamps)
{
int t = (int)Math.Abs(Math.Round((timeStamp.Time - lastIntensityUpdate) / intensityGraphUpdateInterval));
if (t == order)
{
float size = 6;
Vector2 p = new Vector2(pos.X - size / 2, pos.Y - size / 2);
ShapeExtensions.DrawPoint(sBatch, p, color, size);
break;
}
}
}
}
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
new Vector2(graphRect.Right, graphRect.Y + graphRect.Height * (1.0f - eventThreshold)), new Vector2(graphRect.Right, graphRect.Y + graphRect.Height * (1.0f - eventThreshold)),
new Vector2(graphRect.Right + 5, graphRect.Y + graphRect.Height * (1.0f - eventThreshold)), Color.Orange, 0, 1); new Vector2(graphRect.Right + 5, graphRect.Y + graphRect.Height * (1.0f - eventThreshold)), Color.Orange, width: 3);
y = graphRect.Bottom + 20; int yStep = (int)(20 * GUI.yScale);
y = graphRect.Bottom + yStep;
if (isGraphHovered || isGraphSelected)
{
y += yStep;
}
int x = graphRect.X; int x = graphRect.X;
if (isCrewAway && crewAwayDuration < settings.FreezeDurationWhenCrewAway) if (isCrewAway && crewAwayDuration < settings.FreezeDurationWhenCrewAway)
{ {
@@ -143,7 +228,7 @@ namespace Barotrauma
if (CurrentIntensity < eventSet.MinIntensity || CurrentIntensity > eventSet.MaxIntensity) if (CurrentIntensity < eventSet.MinIntensity || CurrentIntensity > eventSet.MaxIntensity)
{ {
GUI.DrawString(spriteBatch, new Vector2(x, y), GUI.DrawString(spriteBatch, new Vector2(x, y),
" intensity between " + ((int) eventSet.MinIntensity) + " and " + ((int) eventSet.MaxIntensity), " intensity between " + eventSet.MinIntensity.FormatDoubleDecimal() + " and " + eventSet.MaxIntensity.FormatDoubleDecimal(),
Color.Orange * 0.8f, null, 0, GUI.SmallFont); Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12; y += 12;
} }
@@ -159,13 +244,13 @@ namespace Barotrauma
if (y > GameMain.GraphicsHeight * 0.9f) if (y > GameMain.GraphicsHeight * 0.9f)
{ {
y = graphRect.Bottom + 35; y = graphRect.Bottom + yStep * 2;
x += 250; x += 300;
} }
} }
GUI.DrawString(spriteBatch, new Vector2(x, y), "Current events: ", Color.White * 0.9f, null, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(x, y), "Current events: ", Color.White * 0.9f, null, 0, GUI.SmallFont);
y += 15; y += yStep;
foreach (Event ev in activeEvents.Where(ev => !ev.IsFinished || PlayerInput.IsShiftDown())) foreach (Event ev in activeEvents.Where(ev => !ev.IsFinished || PlayerInput.IsShiftDown()))
{ {
@@ -182,17 +267,15 @@ namespace Barotrauma
{ {
GUI.MouseCursor = CursorState.Hand; GUI.MouseCursor = CursorState.Hand;
GUI.DrawRectangle(spriteBatch, outlineRect, Color.White); GUI.DrawRectangle(spriteBatch, outlineRect, Color.White);
if (ev != PinnedEvent) if (ev != PinnedEvent)
{ {
DrawEvent(spriteBatch, ev, rect); DrawEvent(spriteBatch, ev, rect);
} }
else if (PlayerInput.SecondaryMouseButtonHeld() || PlayerInput.SecondaryMouseButtonDown()) else if (rightMousePressed)
{ {
PinnedEvent = null; PinnedEvent = null;
} }
if (leftMousePressed)
if (PlayerInput.PrimaryMouseButtonHeld() || PlayerInput.PrimaryMouseButtonDown())
{ {
PinnedEvent = ev; PinnedEvent = ev;
} }
@@ -201,8 +284,8 @@ namespace Barotrauma
y += 18; y += 18;
if (y > GameMain.GraphicsHeight * 0.9f) if (y > GameMain.GraphicsHeight * 0.9f)
{ {
y = graphRect.Bottom + 35; y = graphRect.Bottom + yStep * 2;
x += 250; x += 300;
} }
} }
} }
@@ -352,9 +435,11 @@ namespace Barotrauma
return DrawInfoRectangle(spriteBatch, scriptedEvent, text, parentRect, positions); return DrawInfoRectangle(spriteBatch, scriptedEvent, text, parentRect, positions);
} }
private readonly List<DebugLine> debugPositions = new List<DebugLine>();
private Rectangle DrawArtifactEvent(SpriteBatch spriteBatch, ArtifactEvent artifactEvent, Rectangle? parentRect = null) private Rectangle DrawArtifactEvent(SpriteBatch spriteBatch, ArtifactEvent artifactEvent, Rectangle? parentRect = null)
{ {
List<DebugLine> positions = new List<DebugLine>(); debugPositions.Clear();
string text = $"Finished: {artifactEvent.IsFinished.ColorizeObject()}\n" + string text = $"Finished: {artifactEvent.IsFinished.ColorizeObject()}\n" +
$"Item: {artifactEvent.Item.ColorizeObject()}\n" + $"Item: {artifactEvent.Item.ColorizeObject()}\n" +
@@ -364,15 +449,15 @@ namespace Barotrauma
if (artifactEvent.Item != null && !artifactEvent.Item.Removed) if (artifactEvent.Item != null && !artifactEvent.Item.Removed)
{ {
Vector2 pos = artifactEvent.Item.WorldPosition; Vector2 pos = artifactEvent.Item.WorldPosition;
positions.Add(new DebugLine(pos, Color.White)); debugPositions.Add(new DebugLine(pos, Color.White));
} }
return DrawInfoRectangle(spriteBatch, artifactEvent, text, parentRect, positions); return DrawInfoRectangle(spriteBatch, artifactEvent, text, parentRect, debugPositions);
} }
private Rectangle DrawMonsterEvent(SpriteBatch spriteBatch, MonsterEvent monsterEvent, Rectangle? parentRect = null) private Rectangle DrawMonsterEvent(SpriteBatch spriteBatch, MonsterEvent monsterEvent, Rectangle? parentRect = null)
{ {
List<DebugLine> positions = new List<DebugLine>(); debugPositions.Clear();
string text = $"Finished: {monsterEvent.IsFinished.ColorizeObject()}\n" + string text = $"Finished: {monsterEvent.IsFinished.ColorizeObject()}\n" +
$"Amount: {monsterEvent.MinAmount.ColorizeObject()} - {monsterEvent.MaxAmount.ColorizeObject()}\n" + $"Amount: {monsterEvent.MinAmount.ColorizeObject()} - {monsterEvent.MaxAmount.ColorizeObject()}\n" +
@@ -383,7 +468,7 @@ namespace Barotrauma
{ {
Vector2 pos = monsterEvent.SpawnPos.Value; Vector2 pos = monsterEvent.SpawnPos.Value;
text += $"Distance from submarine: {Vector2.Distance(pos, Submarine.MainSub.WorldPosition).ColorizeObject()}\n"; text += $"Distance from submarine: {Vector2.Distance(pos, Submarine.MainSub.WorldPosition).ColorizeObject()}\n";
positions.Add(new DebugLine(pos, Color.White)); debugPositions.Add(new DebugLine(pos, Color.White));
} }
if (monsterEvent.Monsters != null) if (monsterEvent.Monsters != null)
@@ -394,11 +479,10 @@ namespace Barotrauma
{ {
text += $" {monster.ColorizeObject()} -> (Dead: {monster.IsDead.ColorizeObject()}, Health: {monster.HealthPercentage.ColorizeObject()}%, AIState: {(monster.AIController is EnemyAIController enemyAI ? enemyAI.State : AIState.Idle ).ColorizeObject()})\n"; text += $" {monster.ColorizeObject()} -> (Dead: {monster.IsDead.ColorizeObject()}, Health: {monster.HealthPercentage.ColorizeObject()}%, AIState: {(monster.AIController is EnemyAIController enemyAI ? enemyAI.State : AIState.Idle ).ColorizeObject()})\n";
if (monster.Removed) { continue; } if (monster.Removed) { continue; }
positions.Add(new DebugLine(monster.WorldPosition, Color.Red)); debugPositions.Add(new DebugLine(monster.WorldPosition, Color.Red));
} }
} }
return DrawInfoRectangle(spriteBatch, monsterEvent, text, parentRect, debugPositions);
return DrawInfoRectangle(spriteBatch, monsterEvent, text, parentRect, positions);
} }
private Rectangle DrawInfoRectangle(SpriteBatch spriteBatch, Event @event, string text, Rectangle? parentRect = null, List<DebugLine>? drawPoints = null) private Rectangle DrawInfoRectangle(SpriteBatch spriteBatch, Event @event, string text, Rectangle? parentRect = null, List<DebugLine>? drawPoints = null)
@@ -78,7 +78,7 @@ namespace Barotrauma
CoroutineManager.StartCoroutine(ShowMessageBoxAfterRoundSummary(header, message)); CoroutineManager.StartCoroutine(ShowMessageBoxAfterRoundSummary(header, message));
} }
private IEnumerable<object> ShowMessageBoxAfterRoundSummary(string header, string message) private IEnumerable<CoroutineStatus> ShowMessageBoxAfterRoundSummary(string header, string message)
{ {
while (GUIMessageBox.VisibleBox?.UserData is RoundSummary) while (GUIMessageBox.VisibleBox?.UserData is RoundSummary)
{ {
@@ -51,16 +51,30 @@ namespace Barotrauma
} }
} }
public float LineHeight => baseHeight * 1.8f;
private uint[] charRanges; private uint[] charRanges;
private int texDims; private int texDims;
private uint baseChar; private uint baseChar;
private struct GlyphData private readonly struct GlyphData
{ {
public int texIndex; public readonly int TexIndex;
public Vector2 drawOffset; public readonly Vector2 DrawOffset;
public float advance; public readonly float Advance;
public Rectangle texCoords; public readonly Rectangle TexCoords;
public GlyphData(
int texIndex = default,
Vector2 drawOffset = default,
float advance = default,
Rectangle texCoords = default)
{
TexIndex = texIndex;
DrawOffset = drawOffset;
Advance = advance;
TexCoords = texCoords;
}
} }
public ScalableFont(XElement element, GraphicsDevice gd = null) public ScalableFont(XElement element, GraphicsDevice gd = null)
@@ -167,9 +181,10 @@ namespace Barotrauma
if (face.Glyph.Metrics.HorizontalAdvance > 0) if (face.Glyph.Metrics.HorizontalAdvance > 0)
{ {
//glyph is empty, but char still applies advance //glyph is empty, but char still applies advance
GlyphData blankData = new GlyphData(); GlyphData blankData = new GlyphData(
blankData.advance = (float)face.Glyph.Metrics.HorizontalAdvance; advance: (float)face.Glyph.Metrics.HorizontalAdvance,
blankData.texIndex = -1; //indicates no texture because the glyph is empty texIndex: -1); //indicates no texture because the glyph is empty
texCoords.Add(j, blankData); texCoords.Add(j, blankData);
} }
continue; continue;
@@ -211,13 +226,12 @@ namespace Barotrauma
} }
} }
GlyphData newData = new GlyphData GlyphData newData = new GlyphData(
{ advance: (float)face.Glyph.Metrics.HorizontalAdvance,
advance = (float)face.Glyph.Metrics.HorizontalAdvance, texIndex: texIndex,
texIndex = texIndex, texCoords: new Rectangle((int)currentCoords.X, (int)currentCoords.Y, glyphWidth, glyphHeight),
texCoords = new Rectangle((int)currentCoords.X, (int)currentCoords.Y, glyphWidth, glyphHeight), drawOffset: new Vector2(face.Glyph.BitmapLeft, baseHeight * 14 / 10 - face.Glyph.BitmapTop)
drawOffset = new Vector2(face.Glyph.BitmapLeft, baseHeight * 14 / 10 - face.Glyph.BitmapTop) );
};
texCoords.Add(j, newData); texCoords.Add(j, newData);
for (int y = 0; y < glyphHeight; y++) for (int y = 0; y < glyphHeight; y++)
@@ -278,9 +292,9 @@ namespace Barotrauma
if (face.Glyph.Metrics.HorizontalAdvance > 0) if (face.Glyph.Metrics.HorizontalAdvance > 0)
{ {
//glyph is empty, but char still applies advance //glyph is empty, but char still applies advance
GlyphData blankData = new GlyphData(); GlyphData blankData = new GlyphData(
blankData.advance = (float)face.Glyph.Metrics.HorizontalAdvance; advance: (float)face.Glyph.Metrics.HorizontalAdvance,
blankData.texIndex = -1; //indicates no texture because the glyph is empty texIndex: -1); //indicates no texture because the glyph is empty
texCoords.Add(character, blankData); texCoords.Add(character, blankData);
} }
return; return;
@@ -316,19 +330,18 @@ namespace Barotrauma
currentDynamicPixelBuffer = null; currentDynamicPixelBuffer = null;
} }
GlyphData newData = new GlyphData GlyphData newData = new GlyphData(
{ advance: (float)horizontalAdvance,
advance = (float)horizontalAdvance, texIndex: textures.Count - 1,
texIndex = textures.Count - 1, texCoords: new Rectangle((int)currentDynamicAtlasCoords.X, (int)currentDynamicAtlasCoords.Y, glyphWidth, glyphHeight),
texCoords = new Rectangle((int)currentDynamicAtlasCoords.X, (int)currentDynamicAtlasCoords.Y, glyphWidth, glyphHeight), drawOffset: drawOffset
drawOffset = drawOffset );
};
texCoords.Add(character, newData); texCoords.Add(character, newData);
if (currentDynamicPixelBuffer == null) if (currentDynamicPixelBuffer == null)
{ {
currentDynamicPixelBuffer = new uint[texDims * texDims]; currentDynamicPixelBuffer = new uint[texDims * texDims];
textures[newData.texIndex].GetData<uint>(currentDynamicPixelBuffer, 0, texDims * texDims); textures[newData.TexIndex].GetData<uint>(currentDynamicPixelBuffer, 0, texDims * texDims);
} }
for (int y = 0; y < glyphHeight; y++) for (int y = 0; y < glyphHeight; y++)
@@ -339,12 +352,25 @@ namespace Barotrauma
currentDynamicPixelBuffer[((int)currentDynamicAtlasCoords.X + x) + ((int)currentDynamicAtlasCoords.Y + y) * texDims] = (uint)(byteColor << 24 | 0x00ffffff); currentDynamicPixelBuffer[((int)currentDynamicAtlasCoords.X + x) + ((int)currentDynamicAtlasCoords.Y + y) * texDims] = (uint)(byteColor << 24 | 0x00ffffff);
} }
} }
textures[newData.texIndex].SetData<uint>(currentDynamicPixelBuffer); textures[newData.TexIndex].SetData<uint>(currentDynamicPixelBuffer);
currentDynamicAtlasCoords.X += glyphWidth + 2; currentDynamicAtlasCoords.X += glyphWidth + 2;
} }
} }
private GlyphData GetGlyphData(uint charIndex)
{
const uint DEFAULT_INDEX = 0x25A1; //U+25A1 = white square
if (texCoords.TryGetValue(charIndex, out GlyphData gd) ||
texCoords.TryGetValue(DEFAULT_INDEX, out gd))
{
return gd;
}
return new GlyphData(texIndex: -1);
}
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth) public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
{ {
if (textures.Count == 0 && !DynamicLoading) { return; } if (textures.Count == 0 && !DynamicLoading) { return; }
@@ -358,8 +384,8 @@ namespace Barotrauma
{ {
lineNum++; lineNum++;
currentPos = position; currentPos = position;
currentPos.X -= baseHeight * 1.8f * lineNum * advanceUnit.Y * scale.Y; currentPos.X -= LineHeight * lineNum * advanceUnit.Y * scale.Y;
currentPos.Y += baseHeight * 1.8f * lineNum * advanceUnit.X * scale.Y; currentPos.Y += LineHeight * lineNum * advanceUnit.X * scale.Y;
continue; continue;
} }
@@ -369,19 +395,17 @@ namespace Barotrauma
DynamicRenderAtlas(graphicsDevice, charIndex); DynamicRenderAtlas(graphicsDevice, charIndex);
} }
if (texCoords.TryGetValue(charIndex, out GlyphData gd) || texCoords.TryGetValue(9633, out gd)) //9633 = white square GlyphData gd = GetGlyphData(charIndex);
if (gd.TexIndex >= 0)
{ {
if (gd.texIndex >= 0) Texture2D tex = textures[gd.TexIndex];
{ Vector2 drawOffset;
Texture2D tex = textures[gd.texIndex]; drawOffset.X = gd.DrawOffset.X * advanceUnit.X * scale.X - gd.DrawOffset.Y * advanceUnit.Y * scale.Y;
Vector2 drawOffset; drawOffset.Y = gd.DrawOffset.X * advanceUnit.Y * scale.Y + gd.DrawOffset.Y * advanceUnit.X * scale.X;
drawOffset.X = gd.drawOffset.X * advanceUnit.X * scale.X - gd.drawOffset.Y * advanceUnit.Y * scale.Y;
drawOffset.Y = gd.drawOffset.X * advanceUnit.Y * scale.Y + gd.drawOffset.Y * advanceUnit.X * scale.X;
sb.Draw(tex, currentPos + drawOffset, gd.texCoords, color, rotation, origin, scale, se, layerDepth); sb.Draw(tex, currentPos + drawOffset, gd.TexCoords, color, rotation, origin, scale, se, layerDepth);
}
currentPos += gd.advance * advanceUnit * scale.X;
} }
currentPos += gd.Advance * advanceUnit * scale.X;
} }
} }
@@ -400,7 +424,7 @@ namespace Barotrauma
if (text[i] == '\n') if (text[i] == '\n')
{ {
currentPos.X = position.X; currentPos.X = position.X;
currentPos.Y += baseHeight * 1.8f; currentPos.Y += LineHeight;
continue; continue;
} }
@@ -410,15 +434,13 @@ namespace Barotrauma
DynamicRenderAtlas(graphicsDevice, charIndex); DynamicRenderAtlas(graphicsDevice, charIndex);
} }
if (texCoords.TryGetValue(charIndex, out GlyphData gd) || texCoords.TryGetValue(9633, out gd)) //9633 = white square GlyphData gd = GetGlyphData(charIndex);
if (gd.TexIndex >= 0)
{ {
if (gd.texIndex >= 0) Texture2D tex = textures[gd.TexIndex];
{ sb.Draw(tex, currentPos + gd.DrawOffset, gd.TexCoords, color);
Texture2D tex = textures[gd.texIndex];
sb.Draw(tex, currentPos + gd.drawOffset, gd.texCoords, color);
}
currentPos.X += gd.advance;
} }
currentPos.X += gd.Advance;
} }
} }
@@ -444,8 +466,8 @@ namespace Barotrauma
{ {
lineNum++; lineNum++;
currentPos = position; currentPos = position;
currentPos.X -= baseHeight * 1.8f * lineNum * advanceUnit.Y * scale.Y; currentPos.X -= LineHeight * lineNum * advanceUnit.Y * scale.Y;
currentPos.Y += baseHeight * 1.8f * lineNum * advanceUnit.X * scale.Y; currentPos.Y += LineHeight * lineNum * advanceUnit.X * scale.Y;
continue; continue;
} }
@@ -476,22 +498,116 @@ namespace Barotrauma
currentTextColor = color; currentTextColor = color;
} }
if (texCoords.TryGetValue(charIndex, out GlyphData gd) || texCoords.TryGetValue(9633, out gd)) //9633 = white square GlyphData gd = GetGlyphData(charIndex);
if (gd.TexIndex >= 0)
{ {
if (gd.texIndex >= 0) Texture2D tex = textures[gd.TexIndex];
{ Vector2 drawOffset;
Texture2D tex = textures[gd.texIndex]; drawOffset.X = gd.DrawOffset.X * advanceUnit.X * scale.X - gd.DrawOffset.Y * advanceUnit.Y * scale.Y;
Vector2 drawOffset; drawOffset.Y = gd.DrawOffset.X * advanceUnit.Y * scale.Y + gd.DrawOffset.Y * advanceUnit.X * scale.X;
drawOffset.X = gd.drawOffset.X * advanceUnit.X * scale.X - gd.drawOffset.Y * advanceUnit.Y * scale.Y;
drawOffset.Y = gd.drawOffset.X * advanceUnit.Y * scale.Y + gd.drawOffset.Y * advanceUnit.X * scale.X;
sb.Draw(tex, currentPos + drawOffset, gd.texCoords, currentTextColor, rotation, origin, scale, se, layerDepth); sb.Draw(tex, currentPos + drawOffset, gd.TexCoords, currentTextColor, rotation, origin, scale, se, layerDepth);
}
currentPos += gd.advance * advanceUnit * scale.X;
} }
currentPos += gd.Advance * advanceUnit * scale.X;
} }
} }
public string WrapText(string text, float width)
=> WrapText(text, width, requestCharPos: 0, out _, returnAllCharPositions: false, out _);
public string WrapText(string text, float width, int requestCharPos, out Vector2 requestedCharPos)
=> WrapText(text, width, requestCharPos, out requestedCharPos, returnAllCharPositions: false, out _);
public string WrapText(string text, float width, out Vector2[] allCharPositions)
=> WrapText(text, width, requestCharPos: 0, out _, returnAllCharPositions: true, out allCharPositions);
/// <summary>
/// Wraps a string of text to fit within a given width.
/// Optionally returns the caret position of a certain character,
/// or all of them.
/// </summary>
private string WrapText(string text,
float width,
int requestCharPos,
out Vector2 requestedCharPos,
bool returnAllCharPositions,
out Vector2[] allCharPositions)
{
int currLineStart = 0;
Vector2 currentPos = Vector2.Zero;
Vector2 foundCharPos = Vector2.Zero;
int? lastBreakerIndex = null;
string result = "";
var allCharPos = returnAllCharPositions ? new Vector2[text.Length+1] : null;
for (int i = 0; i < text.Length; i++)
{
//Records the caret position of the current character
void recordCurrentPos()
{
if (i == requestCharPos) { foundCharPos = currentPos; }
if (allCharPos != null) { allCharPos[i] = currentPos; }
}
recordCurrentPos();
//Appends a newline to the result and resets the caret position's X value
void nextLine()
{
result += text[currLineStart..i].Remove("\n") + "\n";
lastBreakerIndex = null;
currentPos.X = 0.0f;
currentPos.Y += LineHeight;
currLineStart = i;
}
//If a newline is found in the source, split immediately
if (text[i] == '\n')
{
nextLine();
continue;
}
//Otherwise, advance based on the width of the current character
GlyphData gd = GetGlyphData(text[i]);
float advance = gd.Advance;
if (currentPos.X + advance >= width)
{
//Advancing based on the last character
//would put us past the max width!
if (i > 0 && char.IsWhiteSpace(text[i]) && !char.IsWhiteSpace(text[i - 1]))
{
//Whitespace immediately after a visible
//character can be shrunk down to fit
advance = width - currentPos.X;
}
else
{
if (lastBreakerIndex.HasValue)
{
//A breaker (whitespace or CJK) was found earlier
//in this line, so let's break the line there
i = lastBreakerIndex.Value + 1;
}
nextLine();
recordCurrentPos(); //must re-record current caret position since we are on a new line now
}
}
currentPos.X += advance;
if (char.IsWhiteSpace(text[i]) || TextManager.IsCJK($"{text[i]}"))
{
lastBreakerIndex = i;
}
}
if (requestCharPos >= text.Length) { foundCharPos = currentPos; }
if (allCharPos != null) { allCharPos[text.Length] = currentPos; }
allCharPositions = allCharPos;
result += text[currLineStart..].Remove("\n");
requestedCharPos = foundCharPos;
return result;
}
public Vector2 MeasureString(string text, bool removeExtraSpacing = false) public Vector2 MeasureString(string text, bool removeExtraSpacing = false)
{ {
if (text == null) if (text == null)
@@ -504,7 +620,7 @@ namespace Barotrauma
if (!removeExtraSpacing) if (!removeExtraSpacing)
{ {
retVal.Y = baseHeight * 1.8f; retVal.Y = LineHeight;
} }
else else
{ {
@@ -516,7 +632,7 @@ namespace Barotrauma
if (text[i] == '\n') if (text[i] == '\n')
{ {
currentLineX = 0.0f; currentLineX = 0.0f;
retVal.Y += baseHeight * 1.8f; retVal.Y += LineHeight;
continue; continue;
} }
uint charIndex = text[i]; uint charIndex = text[i];
@@ -524,10 +640,9 @@ namespace Barotrauma
{ {
DynamicRenderAtlas(graphicsDevice, charIndex); DynamicRenderAtlas(graphicsDevice, charIndex);
} }
if (texCoords.TryGetValue(charIndex, out GlyphData gd))
{ GlyphData gd = GetGlyphData(charIndex);
currentLineX += gd.advance; currentLineX += gd.Advance;
}
retVal.X = Math.Max(retVal.X, currentLineX); retVal.X = Math.Max(retVal.X, currentLineX);
} }
return retVal; return retVal;
@@ -536,15 +651,14 @@ namespace Barotrauma
public Vector2 MeasureChar(char c) public Vector2 MeasureChar(char c)
{ {
Vector2 retVal = Vector2.Zero; Vector2 retVal = Vector2.Zero;
retVal.Y = baseHeight * 1.8f; retVal.Y = LineHeight;
if (DynamicLoading && !texCoords.ContainsKey(c)) if (DynamicLoading && !texCoords.ContainsKey(c))
{ {
DynamicRenderAtlas(graphicsDevice, c); DynamicRenderAtlas(graphicsDevice, c);
} }
if (texCoords.TryGetValue(c, out GlyphData gd))
{ GlyphData gd = GetGlyphData(c);
retVal.X = gd.advance; retVal.X = gd.Advance;
}
return retVal; return retVal;
} }
@@ -494,7 +494,7 @@ namespace Barotrauma
GUIFrame.Parent.Visible = visible; GUIFrame.Parent.Visible = visible;
} }
private IEnumerable<object> UpdateMessageAnimation(GUIComponent message, float animDuration) private IEnumerable<CoroutineStatus> UpdateMessageAnimation(GUIComponent message, float animDuration)
{ {
float timer = 0.0f; float timer = 0.0f;
while (timer < animDuration) while (timer < animDuration)
@@ -399,7 +399,7 @@ namespace Barotrauma
" Max: " + GameMain.PerformanceCounter.DrawTimeGraph.LargestValue().ToString("0.00") + " ms", " Max: " + GameMain.PerformanceCounter.DrawTimeGraph.LargestValue().ToString("0.00") + " ms",
GUI.Style.Green, Color.Black * 0.8f, font: SmallFont); GUI.Style.Green, Color.Black * 0.8f, font: SmallFont);
y += 15; y += 15;
GameMain.PerformanceCounter.DrawTimeGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), null, 0, GUI.Style.Green); GameMain.PerformanceCounter.DrawTimeGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), color: Style.Green);
y += 50; y += 50;
DrawString(spriteBatch, new Vector2(300, y), DrawString(spriteBatch, new Vector2(300, y),
@@ -407,8 +407,8 @@ namespace Barotrauma
" Max: " + GameMain.PerformanceCounter.UpdateTimeGraph.LargestValue().ToString("0.00") + " ms", " Max: " + GameMain.PerformanceCounter.UpdateTimeGraph.LargestValue().ToString("0.00") + " ms",
Color.LightBlue, Color.Black * 0.8f, font: SmallFont); Color.LightBlue, Color.Black * 0.8f, font: SmallFont);
y += 15; y += 15;
GameMain.PerformanceCounter.UpdateTimeGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), null, 0, Color.LightBlue); GameMain.PerformanceCounter.UpdateTimeGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), color: Color.LightBlue);
GameMain.PerformanceCounter.UpdateIterationsGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), 20, 0, GUI.Style.Red); GameMain.PerformanceCounter.UpdateIterationsGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), maxValue: 20, color: Style.Red);
y += 50; y += 50;
foreach (string key in GameMain.PerformanceCounter.GetSavedIdentifiers) foreach (string key in GameMain.PerformanceCounter.GetSavedIdentifiers)
{ {
@@ -941,7 +941,8 @@ namespace Barotrauma
inventoryIndex = updateList.IndexOf(CharacterHUD.HUDFrame); inventoryIndex = updateList.IndexOf(CharacterHUD.HUDFrame);
} }
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || (prevMouseOn == null && !PlayerInput.SecondaryMouseButtonHeld())) if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) ||
(prevMouseOn == null && !PlayerInput.SecondaryMouseButtonHeld() && !Inventory.DraggingItems.Any()))
{ {
for (var i = updateList.Count - 1; i > inventoryIndex; i--) for (var i = updateList.Count - 1; i > inventoryIndex; i--)
{ {
@@ -1052,10 +1053,10 @@ namespace Barotrauma
// Children in list boxes can be interacted with despite not having // Children in list boxes can be interacted with despite not having
// a GUIButton inside of them so instead of hard coding we check if // a GUIButton inside of them so instead of hard coding we check if
// the children can be interacted with by checking their hover state // the children can be interacted with by checking their hover state
if (parent is GUIListBox listBox) if (parent is GUIListBox listBox && c.Parent == listBox.Content)
{ {
if (listBox.DraggedElement != null) { return CursorState.Dragging; } if (listBox.DraggedElement != null) { return CursorState.Dragging; }
if (listBox.CanDragElements) { return CursorState.Move; } if (listBox.CurrentDragMode != GUIListBox.DragMode.NoDragging) { return CursorState.Move; }
if (listBox.HoverCursor != CursorState.Default) if (listBox.HoverCursor != CursorState.Default)
{ {
@@ -1148,7 +1149,7 @@ namespace Barotrauma
{ {
CoroutineManager.StartCoroutine(WaitCursorCoroutine(), "WaitCursorTimeout"); CoroutineManager.StartCoroutine(WaitCursorCoroutine(), "WaitCursorTimeout");
IEnumerable<object> WaitCursorCoroutine() IEnumerable<CoroutineStatus> WaitCursorCoroutine()
{ {
MouseCursor = CursorState.Waiting; MouseCursor = CursorState.Waiting;
var timeOut = DateTime.Now + new TimeSpan(0, 0, waitSeconds); var timeOut = DateTime.Now + new TimeSpan(0, 0, waitSeconds);
@@ -1360,7 +1361,7 @@ namespace Barotrauma
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier * Scale; float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier * Scale;
if (overrideAlpha.HasValue || (dist > visibleRange.Start && dist < visibleRange.End)) if (overrideAlpha.HasValue || visibleRange.Contains(dist))
{ {
float alpha = overrideAlpha ?? MathUtils.Min((dist - visibleRange.Start) / 100.0f, 1.0f - ((dist - visibleRange.End + 100f) / 100.0f), 1.0f); float alpha = overrideAlpha ?? MathUtils.Min((dist - visibleRange.Start) / 100.0f, 1.0f - ((dist - visibleRange.End + 100f) / 100.0f), 1.0f);
Vector2 targetScreenPos = cam.WorldToScreen(worldPosition); Vector2 targetScreenPos = cam.WorldToScreen(worldPosition);
@@ -2254,8 +2255,8 @@ namespace Barotrauma
#region Misc #region Misc
public static void TogglePauseMenu() public static void TogglePauseMenu()
{ {
if (Screen.Selected == GameMain.MainMenuScreen) return; if (Screen.Selected == GameMain.MainMenuScreen) { return; }
if (PreventPauseMenuToggle) return; if (PreventPauseMenuToggle) { return; }
settingsMenuOpen = false; settingsMenuOpen = false;
@@ -2276,162 +2277,121 @@ namespace Barotrauma
Stretch = true, Stretch = true,
RelativeSpacing = 0.05f RelativeSpacing = 0.05f
}; };
new GUIButton(new RectTransform(new Vector2(0.1f, 0.1f), pauseMenuInner.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point((int)(15 * GUI.Scale)) }, new GUIButton(new RectTransform(new Vector2(0.1f, 0.1f), pauseMenuInner.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point((int)(15 * GUI.Scale)) },
"", style: "GUIBugButton") "", style: "GUIBugButton")
{ {
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
ToolTip = TextManager.Get("bugreportbutton"), ToolTip = TextManager.Get("bugreportbutton") + $" (v{GameMain.Version})",
OnClicked = (btn, userdata) => { GameMain.Instance.ShowBugReporter(); return true; } OnClicked = (btn, userdata) => { GameMain.Instance.ShowBugReporter(); return true; }
}; };
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuResume")) CreateButton("PauseMenuResume", buttonContainer, null);
{ CreateButton("PauseMenuSettings", buttonContainer, () => { settingsMenuOpen = !settingsMenuOpen; });
OnClicked = TogglePauseMenu
};
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuSettings"))
{
OnClicked = (btn, userData) =>
{
TogglePauseMenu();
settingsMenuOpen = !settingsMenuOpen;
return true;
}
};
bool IsOutpostLevel() => GameMain.GameSession != null && Level.IsLoadedOutpost; bool IsOutpostLevel() => GameMain.GameSession != null && Level.IsLoadedOutpost;
if (Screen.Selected == GameMain.GameScreen && GameMain.GameSession != null) if (Screen.Selected == GameMain.GameScreen && GameMain.GameSession != null)
{ {
if (GameMain.GameSession.GameMode is SinglePlayerCampaign spMode) if (GameMain.GameSession.GameMode is SinglePlayerCampaign spMode)
{ {
var retryButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuRetry")); CreateButton("PauseMenuRetry", buttonContainer, verificationTextTag: "PauseMenuRetryVerification", action: () =>
retryButton.OnClicked += (btn, userData) =>
{ {
var msgBox = new GUIMessageBox("", TextManager.Get("PauseMenuRetryVerification"), new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") }) if (GameMain.GameSession.RoundSummary?.Frame != null)
{ {
UserData = "verificationprompt" GUIMessageBox.MessageBoxes.Remove(GameMain.GameSession.RoundSummary.Frame);
}; }
msgBox.Buttons[0].OnClicked = (_, userdata) => GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData as string == "ConversationAction");
{ GameMain.GameSession.LoadPreviousSave();
if (GameMain.GameSession.RoundSummary?.Frame != null) });
{
GUIMessageBox.MessageBoxes.Remove(GameMain.GameSession.RoundSummary.Frame);
}
GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData as string == "ConversationAction");
TogglePauseMenu(btn, userData);
GameMain.GameSession.LoadPreviousSave();
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = (_, userdata) =>
{
TogglePauseMenu(btn, userData);
msgBox.Close();
return true;
};
return true;
};
if (IsOutpostLevel()) if (IsOutpostLevel())
{ {
var saveAndQuitButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuSaveQuit")) CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToMainMenuVerification", action: () =>
{ {
UserData = "save", if (IsOutpostLevel()) { GameMain.QuitToMainMenu(save: true); }
OnClicked = (btn, userData) => });
{
pauseMenuOpen = false;
if (IsOutpostLevel())
{
GameMain.QuitToMainMenu(save: true);
}
return true;
}
};
} }
} }
else if (GameMain.GameSession.GameMode is TestGameMode) else if (GameMain.GameSession.GameMode is TestGameMode)
{ {
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), text: TextManager.Get("PauseMenuReturnToEditor")) CreateButton("PauseMenuReturnToEditor", buttonContainer, action: () =>
{ {
OnClicked = (btn, userdata) => GameMain.GameSession?.EndRound("");
{ });
GameMain.GameSession.EndRound("");
pauseMenuOpen = false;
return true;
}
};
} }
else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ManageRound)) else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ManageRound))
{ {
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), bool canSave = GameMain.GameSession.GameMode is CampaignMode && IsOutpostLevel();
text: TextManager.Get(GameMain.GameSession.GameMode is CampaignMode ? "ReturnToServerlobby": "EndRound")) if (canSave)
{ {
OnClicked = (btn, userdata) => CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToServerLobbyVerification", action: () =>
{ {
if (!GameMain.Client.HasPermission(ClientPermissions.ManageRound)) { return false; } GameMain.Client?.RequestRoundEnd(save: true);
if (GameMain.GameSession.GameMode is CampaignMode && !IsOutpostLevel() || (!Submarine.MainSub.AtStartExit && !Submarine.MainSub.AtEndExit)) });
{ }
var msgBox = new GUIMessageBox("",
TextManager.Get(GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd"), CreateButton(GameMain.GameSession.GameMode is CampaignMode ? "ReturnToServerlobby" : "EndRound", buttonContainer,
new string[] { TextManager.Get("Yes"), TextManager.Get("No") }) verificationTextTag: GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
{ action: () =>
UserData = "verificationprompt" {
}; GameMain.Client?.RequestRoundEnd(save: false);
msgBox.Buttons[0].OnClicked = (_, __) => });
{
pauseMenuOpen = false;
GameMain.Client.RequestRoundEnd();
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
}
else
{
pauseMenuOpen = false;
GameMain.Client.RequestRoundEnd();
}
return true;
}
};
} }
} }
var quitButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuQuit")); if (GameMain.GameSession != null || Screen.Selected is CharacterEditorScreen || Screen.Selected is SubEditorScreen)
quitButton.OnClicked += (btn, userData) =>
{ {
if (GameMain.GameSession != null || (Screen.Selected is CharacterEditorScreen || Screen.Selected is SubEditorScreen)) CreateButton("PauseMenuQuit", buttonContainer,
{ verificationTextTag: GameMain.GameSession == null ? "PauseMenuQuitVerificationEditor" : "PauseMenuQuitVerification",
string text = GameMain.GameSession == null ? "PauseMenuQuitVerificationEditor" : "PauseMenuQuitVerification"; action: () =>
var msgBox = new GUIMessageBox("", TextManager.Get(text), new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") })
{
UserData = "verificationprompt"
};
msgBox.Buttons[0].OnClicked = (yesBtn, userdata) =>
{ {
GameMain.QuitToMainMenu(save: false); GameMain.QuitToMainMenu(save: false);
pauseMenuOpen = false; });
return true; }
}; else
msgBox.Buttons[0].OnClicked += msgBox.Close; {
msgBox.Buttons[1].OnClicked = (_, userdata) => CreateButton("PauseMenuQuit", buttonContainer, action: () => { GameMain.QuitToMainMenu(save: false); });
{ }
pauseMenuOpen = false;
msgBox.Close();
return true;
};
}
else
{
GameMain.QuitToMainMenu(save: false);
pauseMenuOpen = false;
}
return true;
};
GUITextBlock.AutoScaleAndNormalize(buttonContainer.Children.Where(c => c is GUIButton).Select(c => ((GUIButton)c).TextBlock)); GUITextBlock.AutoScaleAndNormalize(buttonContainer.Children.Where(c => c is GUIButton).Select(c => ((GUIButton)c).TextBlock));
} }
void CreateButton(string textTag, GUIComponent parent, Action action, string verificationTextTag = null)
{
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), TextManager.Get(textTag))
{
OnClicked = (btn, userData) =>
{
if (string.IsNullOrEmpty(verificationTextTag))
{
pauseMenuOpen = false;
action?.Invoke();
}
else
{
CreateVerificationPrompt(verificationTextTag, action);
}
return true;
}
};
}
void CreateVerificationPrompt(string textTag, Action confirmAction)
{
var msgBox = new GUIMessageBox("", TextManager.Get(textTag),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") })
{
UserData = "verificationprompt"
};
msgBox.Buttons[0].OnClicked = (_, __) =>
{
pauseMenuOpen = false;
confirmAction?.Invoke();
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
}
} }
private static bool TogglePauseMenu(GUIButton button, object obj) private static bool TogglePauseMenu(GUIButton button, object obj)
@@ -531,6 +531,17 @@ namespace Barotrauma
} }
} }
public virtual void ForceLayoutRecalculation()
{
//This is very ugly but it gets the job done, it
//would be real nice to un-jank this some day
ForceUpdate();
ForceUpdate();
foreach (var child in Children) { child.ForceLayoutRecalculation(); }
}
public void ForceUpdate() => Update((float)Timing.Step);
/// <summary> /// <summary>
/// Updates all the children manually. /// Updates all the children manually.
/// </summary> /// </summary>
@@ -831,7 +842,7 @@ namespace Barotrauma
CoroutineManager.StartCoroutine(SlideToPosition(duration, 0.0f, targetPos)); CoroutineManager.StartCoroutine(SlideToPosition(duration, 0.0f, targetPos));
} }
private IEnumerable<object> SlideToPosition(float duration, float wait, Vector2 target) private IEnumerable<CoroutineStatus> SlideToPosition(float duration, float wait, Vector2 target)
{ {
float t = 0.0f; float t = 0.0f;
var (startX, startY) = RectTransform.ScreenSpaceOffset.ToVector2(); var (startX, startY) = RectTransform.ScreenSpaceOffset.ToVector2();
@@ -855,7 +866,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
private IEnumerable<object> LerpAlpha(float to, float duration, bool removeAfter, float wait = 0.0f) private IEnumerable<CoroutineStatus> LerpAlpha(float to, float duration, bool removeAfter, float wait = 0.0f)
{ {
State = ComponentState.None; State = ComponentState.None;
float t = 0.0f; float t = 0.0f;
@@ -894,7 +905,7 @@ namespace Barotrauma
pulsateCoroutine = CoroutineManager.StartCoroutine(DoPulsate(startScale, endScale, duration), "Pulsate" + ToString()); pulsateCoroutine = CoroutineManager.StartCoroutine(DoPulsate(startScale, endScale, duration), "Pulsate" + ToString());
} }
private IEnumerable<object> DoPulsate(Vector2 startScale, Vector2 endScale, float duration) private IEnumerable<CoroutineStatus> DoPulsate(Vector2 startScale, Vector2 endScale, float duration)
{ {
float t = 0.0f; float t = 0.0f;
while (t < duration) while (t < duration)
@@ -34,7 +34,7 @@ namespace Barotrauma
public GUIScrollBar ScrollBar { get; private set; } public GUIScrollBar ScrollBar { get; private set; }
private readonly Dictionary<GUIComponent, bool> childVisible = new Dictionary<GUIComponent, bool>(); private readonly Dictionary<GUIComponent, bool> childVisible = new Dictionary<GUIComponent, bool>();
private int totalSize; private int totalSize;
private bool childrenNeedsRecalculation; private bool childrenNeedsRecalculation;
private bool scrollBarNeedsRecalculation; private bool scrollBarNeedsRecalculation;
@@ -53,7 +53,23 @@ namespace Barotrauma
} }
} }
public bool SelectMultiple; public enum SelectMode
{
SelectSingle,
SelectMultiple,
RequireShiftToSelectMultiple
}
public SelectMode CurrentSelectMode = SelectMode.SelectSingle;
public bool SelectMultiple
{
get { return CurrentSelectMode != SelectMode.SelectSingle; }
set
{
CurrentSelectMode = value ? SelectMode.SelectMultiple : SelectMode.SelectSingle;
}
}
public bool HideChildrenOutsideFrame = true; public bool HideChildrenOutsideFrame = true;
@@ -103,7 +119,7 @@ namespace Barotrauma
/// <summary> /// <summary>
/// true if mouse down should select elements instead of mouse up /// true if mouse down should select elements instead of mouse up
/// </summary> /// </summary>
private bool useMouseDownToSelect = false; private readonly bool useMouseDownToSelect = false;
private Vector4? overridePadding; private Vector4? overridePadding;
public Vector4 Padding public Vector4 Padding
@@ -132,10 +148,7 @@ namespace Barotrauma
// TODO: fix implicit hiding // TODO: fix implicit hiding
public bool Selected { get; set; } public bool Selected { get; set; }
public List<GUIComponent> AllSelected public IReadOnlyList<GUIComponent> AllSelected => selected;
{
get { return selected; }
}
public object SelectedData public object SelectedData
{ {
@@ -214,25 +227,34 @@ namespace Barotrauma
public bool AutoHideScrollBar { get; set; } = true; public bool AutoHideScrollBar { get; set; } = true;
private bool IsScrollBarOnDefaultSide { get; set; } private bool IsScrollBarOnDefaultSide { get; set; }
public bool CanDragElements public enum DragMode
{
NoDragging,
DragWithinBox,
DragOutsideBox
}
private DragMode currentDragMode = DragMode.NoDragging;
public DragMode CurrentDragMode
{ {
get get
{ {
return canDragElements; return currentDragMode;
} }
set set
{ {
if (value == false && canDragElements && draggedElement != null) if (value == DragMode.NoDragging && currentDragMode != DragMode.NoDragging && isDraggingElement)
{ {
DraggedElement = null; DraggedElement = null;
} }
canDragElements = value; currentDragMode = value;
} }
} }
private bool canDragElements = false;
private GUIComponent draggedElement; private GUIComponent draggedElement;
private Rectangle draggedReferenceRectangle; private Point dragMousePosRelativeToTopLeftCorner;
private Point draggedReferenceOffset; private bool isDraggingElement => draggedElement != null;
public bool HasDraggedElementIndexChanged { get; private set; } public bool HasDraggedElementIndexChanged { get; private set; }
public GUIComponent DraggedElement public GUIComponent DraggedElement
@@ -246,8 +268,24 @@ namespace Barotrauma
if (value == draggedElement) { return; } if (value == draggedElement) { return; }
draggedElement = value; draggedElement = value;
HasDraggedElementIndexChanged = false; HasDraggedElementIndexChanged = false;
if (value == null) { return; }
dragMousePosRelativeToTopLeftCorner = PlayerInput.MousePosition.ToPoint() - value.Rect.Location;
if (SelectMultiple)
{
if (!AllSelected.Contains(DraggedElement))
{
Select(DraggedElement.ToEnumerable());
}
}
} }
} }
//This exists to work around the fact that rendering child
//elements on top of the listbox's siblings is a clusterfuck.
public bool HideDraggedElement = false;
private readonly bool isHorizontal; private readonly bool isHorizontal;
@@ -354,7 +392,7 @@ namespace Barotrauma
(child.UserData == null && userData == null)) (child.UserData == null && userData == null))
{ {
Select(i, force, autoScroll); Select(i, force, autoScroll);
if (!SelectMultiple) return; if (!SelectMultiple) { return; }
} }
i++; i++;
} }
@@ -363,9 +401,10 @@ namespace Barotrauma
private Point CalculateFrameSize(bool isHorizontal, int scrollBarSize) private Point CalculateFrameSize(bool isHorizontal, int scrollBarSize)
=> isHorizontal ? new Point(Rect.Width, Rect.Height - scrollBarSize) : new Point(Rect.Width - scrollBarSize, Rect.Height); => isHorizontal ? new Point(Rect.Width, Rect.Height - scrollBarSize) : new Point(Rect.Width - scrollBarSize, Rect.Height);
private void RepositionChildren() public Vector2 CalculateTopOffset()
{ {
int x = 0, y = 0; int x = 0;
int y = 0;
if (ScrollBar.BarSize < 1.0f) if (ScrollBar.BarSize < 1.0f)
{ {
if (ScrollBar.IsHorizontal) if (ScrollBar.IsHorizontal)
@@ -378,53 +417,59 @@ namespace Barotrauma
} }
} }
return new Vector2(x, y);
}
private void CalculateChildrenOffsets(Action<int, Point> callback)
{
Vector2 topOffset = CalculateTopOffset();
int x = (int)topOffset.X;
int y = (int)topOffset.Y;
for (int i = 0; i < Content.CountChildren; i++) for (int i = 0; i < Content.CountChildren; i++)
{ {
GUIComponent child = Content.GetChild(i); GUIComponent child = Content.GetChild(i);
if (!child.Visible) { continue; } if (!child.Visible) { continue; }
if (RectTransform != null) if (RectTransform != null)
{ {
if (child != draggedElement && (child.RectTransform.AbsoluteOffset.X != x || child.RectTransform.AbsoluteOffset.Y != y)) callback(i, new Point(x, y));
{
child.RectTransform.AbsoluteOffset = new Point(x, y);
}
} }
if (useGridLayout) if (useGridLayout)
{ {
void advanceGridLayout(
ref int primaryCoord,
ref int secondaryCoord,
int primaryChildDimension,
int secondaryChildDimension,
int primaryParentDimension)
{
if (primaryCoord + primaryChildDimension + Spacing > primaryParentDimension)
{
primaryCoord = 0;
secondaryCoord += secondaryChildDimension + Spacing;
callback(i, new Point(x, y));
}
primaryCoord += primaryChildDimension + Spacing;
}
if (ScrollBar.IsHorizontal) if (ScrollBar.IsHorizontal)
{ {
if (y + child.Rect.Height + Spacing > Content.Rect.Height) advanceGridLayout(
{ primaryCoord: ref y,
y = 0; secondaryCoord: ref x,
x += child.Rect.Width + Spacing; primaryChildDimension: child.Rect.Height,
if (child != draggedElement && (child.RectTransform.AbsoluteOffset.X != x || child.RectTransform.AbsoluteOffset.Y != y)) secondaryChildDimension: child.Rect.Width,
{ primaryParentDimension: Content.Rect.Height);
child.RectTransform.AbsoluteOffset = new Point(x, y);
}
y += child.Rect.Height + Spacing;
}
else
{
y += child.Rect.Height + Spacing;
}
} }
else else
{ {
if (x + child.Rect.Width + Spacing > Content.Rect.Width) advanceGridLayout(
{ primaryCoord: ref x,
x = 0; secondaryCoord: ref y,
y += child.Rect.Height + Spacing; primaryChildDimension: child.Rect.Width,
if (child != draggedElement && (child.RectTransform.AbsoluteOffset.X != x || child.RectTransform.AbsoluteOffset.Y != y)) secondaryChildDimension: child.Rect.Height,
{ primaryParentDimension: Content.Rect.Width);
child.RectTransform.AbsoluteOffset = new Point(x, y);
}
x += child.Rect.Width + Spacing;
}
else
{
x += child.Rect.Width + Spacing;
}
} }
} }
else else
@@ -440,6 +485,18 @@ namespace Barotrauma
} }
} }
} }
private void RepositionChildren()
{
CalculateChildrenOffsets((index, offset) =>
{
var child = Content.GetChild(index);
if (child != draggedElement && child.RectTransform.AbsoluteOffset != offset)
{
child.RectTransform.AbsoluteOffset = offset;
}
});
}
/// <summary> /// <summary>
/// Scrolls the list to the specific element, currently only works when smooth scrolling and PadBottom are enabled. /// Scrolls the list to the specific element, currently only works when smooth scrolling and PadBottom are enabled.
@@ -466,7 +523,7 @@ namespace Barotrauma
{ {
CoroutineManager.StartCoroutine(ScrollCoroutine()); CoroutineManager.StartCoroutine(ScrollCoroutine());
IEnumerable<object> ScrollCoroutine() IEnumerable<CoroutineStatus> ScrollCoroutine()
{ {
if (BarSize >= 1.0f) if (BarSize >= 1.0f)
{ {
@@ -490,68 +547,122 @@ namespace Barotrauma
} }
} }
private void StartDraggingElement(GUIComponent child)
private void UpdateChildrenRect()
{ {
//dragging DraggedElement = child;
if (CanDragElements && draggedElement != null) }
private bool UpdateDragging()
{
if (CurrentDragMode == DragMode.NoDragging || !isDraggingElement) { return false; }
if (!PlayerInput.PrimaryMouseButtonHeld())
{ {
if (!PlayerInput.PrimaryMouseButtonHeld()) var draggedElem = draggedElement;
OnRearranged?.Invoke(this, draggedElem.UserData);
DraggedElement = null;
RepositionChildren();
if (AllSelected.Contains(draggedElem)) { return true; }
}
else
{
Vector2 topOffset = CalculateTopOffset();
var mousePos = PlayerInput.MousePosition.ToPoint();
draggedElement.RectTransform.AbsoluteOffset = mousePos - Content.Rect.Location - dragMousePosRelativeToTopLeftCorner;
if (CurrentDragMode != DragMode.DragOutsideBox)
{ {
OnRearranged?.Invoke(this, draggedElement.UserData); var offset = draggedElement.RectTransform.AbsoluteOffset;
DraggedElement = null; draggedElement.RectTransform.AbsoluteOffset =
RepositionChildren(); isHorizontal ? new Point(offset.X, 0) : new Point(0, offset.Y);
}
int index = Content.RectTransform.GetChildIndex(draggedElement.RectTransform);
int newIndex = index;
Point draggedOffsetWhenReleased = Point.Zero;
CalculateChildrenOffsets((i, offset) =>
{
if (index != i) { return; }
draggedOffsetWhenReleased = offset;
});
Rectangle draggedRectWhenReleased = new Rectangle(Content.Rect.Location + draggedOffsetWhenReleased, draggedElement.Rect.Size);
void shiftIndices(
float mousePos,
ref int draggedRectWhenReleasedLocation,
int draggedRectWhenReleasedSize)
{
while (mousePos > (draggedRectWhenReleasedLocation + draggedRectWhenReleasedSize) && newIndex < Content.CountChildren-1)
{
newIndex++;
draggedRectWhenReleasedLocation += draggedRectWhenReleasedSize;
}
while (mousePos < draggedRectWhenReleasedLocation && newIndex > 0)
{
newIndex--;
draggedRectWhenReleasedLocation -= draggedRectWhenReleasedSize;
}
if (newIndex != index && AllSelected.Count > 1)
{
this.selected.Sort((a, b) => Content.GetChildIndex(a) - Content.GetChildIndex(b));
int draggedPos = AllSelected.IndexOf(draggedElement);
if (newIndex < draggedPos)
{
newIndex = draggedPos;
}
if (newIndex >= Content.CountChildren - (AllSelected.Count - draggedPos))
{
int max = Content.CountChildren - (AllSelected.Count - draggedPos);
newIndex = max;
}
}
}
if (isHorizontal)
{
shiftIndices(
mousePos.X,
ref draggedRectWhenReleased.X,
draggedRectWhenReleased.Width);
} }
else else
{ {
draggedElement.RectTransform.AbsoluteOffset = isHorizontal ? shiftIndices(
draggedReferenceOffset + new Point((int)PlayerInput.MousePosition.X - draggedReferenceRectangle.Center.X, 0) : mousePos.Y,
draggedReferenceOffset + new Point(0, (int)PlayerInput.MousePosition.Y - draggedReferenceRectangle.Center.Y); ref draggedRectWhenReleased.Y,
draggedRectWhenReleased.Height);
}
int index = Content.RectTransform.GetChildIndex(draggedElement.RectTransform); if (newIndex != index)
int currIndex = index; {
if (AllSelected.Count > 1)
if (isHorizontal)
{ {
while (currIndex > 0 && PlayerInput.MousePosition.X < draggedReferenceRectangle.Left) this.selected.Sort((a, b) => Content.GetChildIndex(a) - Content.GetChildIndex(b));
int indexOfDraggedElem = AllSelected.IndexOf(draggedElement);
IEnumerable<GUIComponent> allSelected = AllSelected;
if (newIndex > index) { allSelected = allSelected.Reverse(); }
foreach (var elem in allSelected)
{ {
currIndex--; elem.RectTransform.RepositionChildInHierarchy(newIndex + AllSelected.IndexOf(elem) - indexOfDraggedElem);
draggedReferenceRectangle.X -= draggedReferenceRectangle.Width;
draggedReferenceOffset.X -= draggedReferenceRectangle.Width;
}
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.X > draggedReferenceRectangle.Right)
{
currIndex++;
draggedReferenceRectangle.X += draggedReferenceRectangle.Width;
draggedReferenceOffset.X += draggedReferenceRectangle.Width;
} }
} }
else else
{ {
while (currIndex > 0 && PlayerInput.MousePosition.Y < draggedReferenceRectangle.Top) draggedElement.RectTransform.RepositionChildInHierarchy(newIndex);
{
currIndex--;
draggedReferenceRectangle.Y -= draggedReferenceRectangle.Height;
draggedReferenceOffset.Y -= draggedReferenceRectangle.Height;
}
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.Y > draggedReferenceRectangle.Bottom)
{
currIndex++;
draggedReferenceRectangle.Y += draggedReferenceRectangle.Height;
draggedReferenceOffset.Y += draggedReferenceRectangle.Height;
}
} }
HasDraggedElementIndexChanged = true;
if (currIndex != index)
{
draggedElement.RectTransform.RepositionChildInHierarchy(currIndex);
HasDraggedElementIndexChanged = true;
}
return;
} }
return true;
} }
return false;
}
private void UpdateChildrenRect()
{
if (UpdateDragging()) { return; }
if (SelectTop) if (SelectTop)
{ {
foreach (GUIComponent child in Content.Children) foreach (GUIComponent child in Content.Children)
@@ -581,7 +692,7 @@ namespace Barotrauma
for (int i = 0; i < Content.CountChildren; i++) for (int i = 0; i < Content.CountChildren; i++)
{ {
var child = Content.RectTransform.GetChild(i)?.GUIComponent; var child = Content.RectTransform.GetChild(i)?.GUIComponent;
if (child == null || !child.Visible) { continue; } if (!(child is { Visible: true })) { continue; }
// selecting // selecting
if (Enabled && (CanBeFocused || CanInteractWhenUnfocusable) && child.CanBeFocused && child.Rect.Contains(PlayerInput.MousePosition) && GUI.IsMouseOn(child)) if (Enabled && (CanBeFocused || CanInteractWhenUnfocusable) && child.CanBeFocused && child.Rect.Contains(PlayerInput.MousePosition) && GUI.IsMouseOn(child))
@@ -595,19 +706,15 @@ namespace Barotrauma
if (SelectTop) if (SelectTop)
{ {
ScrollToElement(child); ScrollToElement(child);
Select(i, autoScroll: false, takeKeyBoardFocus: true);
}
else
{
Select(i, autoScroll: false, takeKeyBoardFocus: true);
} }
Select(i, autoScroll: false, takeKeyBoardFocus: true);
} }
if (CanDragElements && PlayerInput.PrimaryMouseButtonDown() && GUI.MouseOn == child) if (CurrentDragMode != DragMode.NoDragging
&& (CurrentSelectMode != SelectMode.RequireShiftToSelectMultiple || (!PlayerInput.IsShiftDown() && !PlayerInput.IsCtrlDown()))
&& PlayerInput.PrimaryMouseButtonDown() && GUI.MouseOn == child)
{ {
DraggedElement = child; StartDraggingElement(child);
draggedReferenceRectangle = child.Rect;
draggedReferenceOffset = child.RectTransform.AbsoluteOffset;
} }
} }
else if (selected.Contains(child)) else if (selected.Contains(child))
@@ -686,6 +793,13 @@ namespace Barotrauma
OnAddedToGUIUpdateList?.Invoke(this); OnAddedToGUIUpdateList?.Invoke(this);
} }
public override void ForceLayoutRecalculation()
{
base.ForceLayoutRecalculation();
Content.ForceLayoutRecalculation();
ScrollBar.ForceLayoutRecalculation();
}
public void RecalculateChildren() public void RecalculateChildren()
{ {
foreach (GUIComponent child in Content.Children) foreach (GUIComponent child in Content.Children)
@@ -709,8 +823,6 @@ namespace Barotrauma
} }
} }
public void ForceUpdate() => Update((float)Timing.Step);
protected override void Update(float deltaTime) protected override void Update(float deltaTime)
{ {
if (!Visible) { return; } if (!Visible) { return; }
@@ -805,7 +917,7 @@ namespace Barotrauma
} }
else else
{ {
ScrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * BarSize; ScrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * ScrollBar.UnclampedBarSize;
} }
} }
@@ -870,6 +982,7 @@ namespace Barotrauma
if (childIndex >= Content.CountChildren || childIndex < 0) { return; } if (childIndex >= Content.CountChildren || childIndex < 0) { return; }
GUIComponent child = Content.GetChild(childIndex); GUIComponent child = Content.GetChild(childIndex);
if (child is null) { return; }
bool wasSelected = true; bool wasSelected = true;
if (OnSelected != null) if (OnSelected != null)
@@ -880,7 +993,8 @@ namespace Barotrauma
if (!wasSelected) { return; } if (!wasSelected) { return; }
if (SelectMultiple) if (CurrentSelectMode == SelectMode.SelectMultiple ||
(CurrentSelectMode == SelectMode.RequireShiftToSelectMultiple && PlayerInput.IsCtrlDown()))
{ {
if (selected.Contains(child)) if (selected.Contains(child))
{ {
@@ -891,6 +1005,23 @@ namespace Barotrauma
selected.Add(child); selected.Add(child);
} }
} }
else if (CurrentSelectMode == SelectMode.RequireShiftToSelectMultiple && PlayerInput.IsShiftDown())
{
var first = SelectedComponent ?? child;
var last = child;
int firstIndex = Content.GetChildIndex(first);
int lastIndex = Content.GetChildIndex(last);
int sgn = Math.Sign(lastIndex - firstIndex);
selected.Clear(); selected.Add(first);
for (int i = firstIndex + sgn; i != lastIndex; i += sgn)
{
if (Content.GetChild(i) is { Visible: true } interChild)
{
selected.Add(interChild);
}
}
if (first != last) { selected.Add(last); }
}
else else
{ {
selected.Clear(); selected.Clear();
@@ -937,6 +1068,14 @@ namespace Barotrauma
} }
} }
public void Select(IEnumerable<GUIComponent> children)
{
Selected = true;
selected.Clear();
selected.AddRange(children.Where(c => Content.Children.Contains(c)));
foreach (var child in selected) { OnSelected?.Invoke(child, child.UserData); }
}
public void Deselect() public void Deselect()
{ {
Selected = false; Selected = false;
@@ -1007,9 +1146,12 @@ namespace Barotrauma
} }
float minScrollBarSize = 20.0f; float minScrollBarSize = 20.0f;
ScrollBar.UnclampedBarSize = ScrollBar.IsHorizontal ?
Math.Min(Content.Rect.Width / (float)totalSize, 1.0f) :
Math.Min(Content.Rect.Height / (float)totalSize, 1.0f);
ScrollBar.BarSize = ScrollBar.IsHorizontal ? ScrollBar.BarSize = ScrollBar.IsHorizontal ?
Math.Max(Math.Min(Content.Rect.Width / (float)totalSize, 1.0f), minScrollBarSize / Content.Rect.Width) : Math.Max(ScrollBar.UnclampedBarSize, minScrollBarSize / Content.Rect.Width) :
Math.Max(Math.Min(Content.Rect.Height / (float)totalSize, 1.0f), minScrollBarSize / Content.Rect.Height); Math.Max(ScrollBar.UnclampedBarSize, minScrollBarSize / Content.Rect.Height);
} }
public override void ClearChildren() public override void ClearChildren()
@@ -1052,10 +1194,11 @@ namespace Barotrauma
int i = 0; int i = 0;
foreach (GUIComponent child in Content.Children) foreach (GUIComponent child in Content.Children)
{ {
if (!child.Visible) continue; if (!child.Visible) { continue; }
if (child == draggedElement && CurrentDragMode == DragMode.DragOutsideBox) { continue; }
if (!IsChildInsideFrame(child)) if (!IsChildInsideFrame(child))
{ {
if (lastVisible > 0) break; if (lastVisible > 0) { break; }
continue; continue;
} }
lastVisible = i; lastVisible = i;
@@ -1070,6 +1213,11 @@ namespace Barotrauma
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable); spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
} }
if (isDraggingElement && CurrentDragMode == DragMode.DragOutsideBox && !HideDraggedElement)
{
draggedElement.DrawManually(spriteBatch, alsoChildren: true, recursive: true);
}
if (ScrollBarVisible) if (ScrollBarVisible)
{ {
ScrollBar.DrawManually(spriteBatch, alsoChildren: true, recursive: true); ScrollBar.DrawManually(spriteBatch, alsoChildren: true, recursive: true);
@@ -183,6 +183,11 @@ namespace Barotrauma
} }
} }
/// <summary>
/// ListBoxes with lots of content in them clamp the size of the scrollbar above a certain minimum size; this is the relative bar size without the clamping applied.
/// </summary>
public float UnclampedBarSize;
public float BarSize public float BarSize
{ {
get { return barSize; } get { return barSize; }
@@ -299,9 +304,15 @@ namespace Barotrauma
} }
else else
{ {
float barScale = 1.0f;
if (UnclampedBarSize > 0.0f)
{
barScale = (UnclampedBarSize / BarSize);
}
MoveButton(new Vector2( MoveButton(new Vector2(
Math.Sign(PlayerInput.MousePosition.X - Bar.Rect.Center.X) * Bar.Rect.Width, Math.Sign(PlayerInput.MousePosition.X - Bar.Rect.Center.X) * Bar.Rect.Width * barScale,
Math.Sign(PlayerInput.MousePosition.Y - Bar.Rect.Center.Y) * Bar.Rect.Height)); Math.Sign(PlayerInput.MousePosition.Y - Bar.Rect.Center.Y) * Bar.Rect.Height * barScale));
} }
} }
} }
@@ -44,6 +44,8 @@ namespace Barotrauma
public UISprite PingCircle { get; private set; } public UISprite PingCircle { get; private set; }
public UISprite YouAreHereCircle { get; private set; }
public UISprite UIGlowCircular { get; private set; } public UISprite UIGlowCircular { get; private set; }
public UISprite UIGlowSolidCircular { get; private set; } public UISprite UIGlowSolidCircular { get; private set; }
@@ -253,6 +255,9 @@ namespace Barotrauma
case "pingcircle": case "pingcircle":
PingCircle = new UISprite(subElement); PingCircle = new UISprite(subElement);
break; break;
case "youareherecircle":
YouAreHereCircle = new UISprite(subElement);
break;
case "radiation": case "radiation":
RadiationSprite = new UISprite(subElement); RadiationSprite = new UISprite(subElement);
break; break;
@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
namespace Barotrauma namespace Barotrauma
@@ -376,6 +377,7 @@ namespace Barotrauma
public void SetTextPos() public void SetTextPos()
{ {
cachedCaretPositions = ImmutableArray<Vector2>.Empty;
if (text == null) { return; } if (text == null) { return; }
censoredText = string.IsNullOrEmpty(text) ? "" : new string('\u2022', text.Length); censoredText = string.IsNullOrEmpty(text) ? "" : new string('\u2022', text.Length);
@@ -389,7 +391,7 @@ namespace Barotrauma
if (Wrap && rect.Width > 0) if (Wrap && rect.Width > 0)
{ {
wrappedText = ToolBox.WrapText(text, rect.Width - padding.X - padding.Z, Font, textScale, playerInput); wrappedText = ToolBox.WrapText(text, rect.Width - padding.X - padding.Z, Font, textScale);
TextSize = MeasureText(wrappedText); TextSize = MeasureText(wrappedText);
} }
else if (OverflowClip) else if (OverflowClip)
@@ -477,108 +479,49 @@ namespace Barotrauma
disabledTextColor = color; disabledTextColor = color;
} }
protected List<Tuple<Vector2, int>> GetAllPositions() private ImmutableArray<Vector2> cachedCaretPositions = ImmutableArray<Vector2>.Empty;
public ImmutableArray<Vector2> GetAllCaretPositions()
{ {
float halfHeight = Font.MeasureString("T").Y * 0.5f * textScale; if (cachedCaretPositions.Any())
string textDrawn = Censor ? CensoredText : WrappedText;
var positions = new List<Tuple<Vector2, int>>();
if (textDrawn.Contains("\n"))
{ {
string[] lines = textDrawn.Split('\n'); return cachedCaretPositions;
int index = 0;
int totalIndex = 0;
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
totalIndex += line.Length;
float totalTextHeight = Font.MeasureString(textDrawn.Substring(0, totalIndex)).Y * textScale;
for (int j = 0; j <= line.Length; j++)
{
Vector2 lineTextSize = Font.MeasureString(line.Substring(0, j)) * textScale;
Vector2 indexPos = new Vector2(lineTextSize.X, totalTextHeight - halfHeight) + TextPos - Origin * textScale;
//DebugConsole.NewMessage($"index: {index}, pos: {indexPos}", Color.AliceBlue);
positions.Add(new Tuple<Vector2, int>(indexPos, index + j));
}
index = totalIndex;
}
} }
else string textDrawn = Censor ? CensoredText : Text;
{ float w = Wrap
textDrawn = Censor ? CensoredText : Text; ? (Rect.Width - Padding.X - Padding.Z) / TextScale
for (int i = 0; i <= Text.Length; i++) : float.PositiveInfinity;
{ Font.WrapText(textDrawn, w, out Vector2[] positions);
Vector2 textSize = Font.MeasureString(textDrawn.Substring(0, i)) * textScale; cachedCaretPositions = positions.Select(p => p * TextScale + TextPos - Origin * TextScale).ToImmutableArray();
Vector2 indexPos = new Vector2(textSize.X, textSize.Y - halfHeight) + TextPos - Origin * textScale; return cachedCaretPositions;
//DebugConsole.NewMessage($"index: {i}, pos: {indexPos}", Color.WhiteSmoke);
positions.Add(new Tuple<Vector2, int>(indexPos, i));
}
}
return positions;
} }
public int GetCaretIndexFromScreenPos(Vector2 pos) public int GetCaretIndexFromScreenPos(in Vector2 pos)
{ {
return GetCaretIndexFromLocalPos(pos - Rect.Location.ToVector2()); return GetCaretIndexFromLocalPos(pos - Rect.Location.ToVector2());
} }
public int GetCaretIndexFromLocalPos(Vector2 pos) public int GetCaretIndexFromLocalPos(in Vector2 pos)
{ {
var positions = GetAllPositions(); var positions = GetAllCaretPositions();
if (positions.Count == 0) { return 0; } if (positions.Length == 0) { return 0; }
float halfHeight = Font.MeasureString("T").Y * 0.5f * textScale;
var currPosition = positions[0]; float closestXDist = float.PositiveInfinity;
float closestYDist = float.PositiveInfinity;
float topY = positions.Min(p => p.Item1.Y); int closestIndex = -1;
for (int i = 0; i < positions.Length; i++)
for (int i = 1; i < positions.Count; i++)
{ {
var p1 = positions[i]; float xDist = Math.Abs(pos.X - positions[i].X);
var p2 = currPosition; float yDist = Math.Abs(pos.Y - (positions[i].Y + Font.LineHeight * 0.5f));
if (yDist < closestYDist || (MathUtils.NearlyEqual(yDist, closestYDist) && xDist < closestXDist))
float diffY = Math.Abs(p1.Item1.Y - pos.Y) - Math.Abs(p2.Item1.Y - pos.Y);
if (diffY < -3.0f)
{ {
currPosition = p1; closestIndex = i;
continue; closestXDist = xDist;
} closestYDist = yDist;
else if (diffY > 3.0f)
{
continue;
}
else
{
diffY = Math.Abs(p1.Item1.Y - pos.Y);
if (diffY < halfHeight || (p1.Item1.Y == topY && pos.Y < topY))
{
//we are on this line, select the nearest character
float diffX = Math.Abs(p1.Item1.X - pos.X) - Math.Abs(p2.Item1.X - pos.X);
if (diffX < -1.0f)
{
currPosition = p1; continue;
}
else
{
continue;
}
}
else
{
//we are on a different line, preserve order
if (p1.Item2 < p2.Item2)
{
if (p1.Item1.Y > pos.Y) { currPosition = p1; }
}
else if (p1.Item2 > p2.Item2)
{
if (p1.Item1.Y < pos.Y) { currPosition = p1; }
}
continue;
}
} }
} }
//GUI.AddMessage($"index: {posIndex.Item2}, pos: {posIndex.Item1}", Color.WhiteSmoke);
return currPosition != null ? currPosition.Item2 : Text.Length; return closestIndex >= 0 ? closestIndex : Text.Length;
} }
protected override void Update(float deltaTime) protected override void Update(float deltaTime)
@@ -44,7 +44,7 @@ namespace Barotrauma
private int? maxTextLength; private int? maxTextLength;
private int _caretIndex; private int _caretIndex;
private int CaretIndex public int CaretIndex
{ {
get { return _caretIndex; } get { return _caretIndex; }
set set
@@ -343,34 +343,23 @@ namespace Barotrauma
private void CalculateCaretPos() private void CalculateCaretPos()
{ {
string textDrawn = Censor ? textBlock.CensoredText : textBlock.WrappedText; if (Censor || !Wrap)
if (textDrawn.Contains("\n"))
{ {
string[] lines = textDrawn.Split('\n'); string textDrawn = textBlock.CensoredText;
int totalIndex = 0; CaretIndex = Math.Min(CaretIndex, textDrawn.Length);
for (int i = 0; i < lines.Length; i++) textDrawn = Censor ? textBlock.CensoredText : textBlock.Text;
{ Vector2 textSize = Font.MeasureString(textDrawn[..CaretIndex]) * TextBlock.TextScale;
int currentLineLength = lines[i].Length; caretPos = new Vector2(textSize.X, 0) + textBlock.TextPos - textBlock.Origin * TextBlock.TextScale;
totalIndex += currentLineLength;
// The caret is on this line
if (CaretIndex < totalIndex || totalIndex == textBlock.Text.Length)
{
int diff = totalIndex - CaretIndex;
int index = currentLineLength - diff;
Vector2 lineTextSize = Font.MeasureString(lines[i].Substring(0, index)) * TextBlock.TextScale;
Vector2 lastLineSize = Font.MeasureString(lines[i]) * TextBlock.TextScale;
float totalTextHeight = Font.MeasureString(textDrawn.Substring(0, totalIndex)).Y * TextBlock.TextScale;
caretPos = new Vector2(lineTextSize.X, totalTextHeight - lastLineSize.Y) + textBlock.TextPos - textBlock.Origin * TextBlock.TextScale;
break;
}
}
} }
else else
{ {
CaretIndex = Math.Min(CaretIndex, textDrawn.Length); CaretIndex = Math.Min(CaretIndex, textBlock.Text.Length);
textDrawn = Censor ? textBlock.CensoredText : textBlock.Text; textBlock.Font.WrapText(
Vector2 textSize = Font.MeasureString(textDrawn.Substring(0, CaretIndex)) * TextBlock.TextScale; textBlock.Text,
caretPos = new Vector2(textSize.X, 0) + textBlock.TextPos - textBlock.Origin * TextBlock.TextScale; (textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z) / TextBlock.TextScale,
CaretIndex,
out Vector2 requestedCharPos);
caretPos = requestedCharPos * TextBlock.TextScale + textBlock.TextPos - textBlock.Origin * TextBlock.TextScale;
} }
caretPosDirty = false; caretPosDirty = false;
} }
@@ -383,6 +372,7 @@ namespace Barotrauma
memento.Store(Text); memento.Store(Text);
} }
CaretIndex = forcedCaretIndex == - 1 ? textBlock.GetCaretIndexFromScreenPos(PlayerInput.MousePosition) : forcedCaretIndex; CaretIndex = forcedCaretIndex == - 1 ? textBlock.GetCaretIndexFromScreenPos(PlayerInput.MousePosition) : forcedCaretIndex;
CalculateCaretPos();
ClearSelection(); ClearSelection();
selected = true; selected = true;
GUI.KeyboardDispatcher.Subscriber = this; GUI.KeyboardDispatcher.Subscriber = this;
@@ -538,59 +528,37 @@ namespace Barotrauma
if (textBlock.WrappedText.Contains("\n")) if (textBlock.WrappedText.Contains("\n"))
{ {
// Multiline selection // Multiline selection
string[] lines = textBlock.WrappedText.Split('\n'); var characterPositions = textBlock.GetAllCaretPositions();
int totalIndex = 0; (int startIndex, int endIndex) = selectionStartIndex < selectionEndIndex
int previousCharacters = 0; ? (selectionStartIndex, selectionEndIndex)
Vector2 offset = textBlock.TextPos - textBlock.Origin; : (selectionEndIndex, selectionStartIndex);
for (int i = 0; i < lines.Length; i++) endIndex--;
void drawRect(Vector2 topLeft, Vector2 bottomRight)
{ {
string currentLine = lines[i]; int minWidth = GUI.IntScale(5);
int currentLineLength = currentLine.Length; if (bottomRight.X - topLeft.X < minWidth) { bottomRight.X = topLeft.X + minWidth; }
totalIndex += currentLineLength; GUI.DrawRectangle(spriteBatch,
bool containsSelection = IsLeftToRight Rect.Location.ToVector2() + topLeft,
? selectionStartIndex < totalIndex && selectionEndIndex > previousCharacters bottomRight - topLeft,
: selectionEndIndex < totalIndex && selectionStartIndex > previousCharacters; SelectionColor, isFilled: true);
if (containsSelection)
{
Vector2 currentLineSize = Font.MeasureString(currentLine) * TextBlock.TextScale;
if ((IsLeftToRight && selectionStartIndex < previousCharacters && selectionEndIndex > totalIndex)
|| !IsLeftToRight && selectionEndIndex < previousCharacters && selectionStartIndex > totalIndex)
{
// select the whole line
Vector2 topLeft = offset + new Vector2(0, currentLineSize.Y * i);
GUI.DrawRectangle(spriteBatch, Rect.Location.ToVector2() + topLeft, currentLineSize, SelectionColor, isFilled: true);
}
else
{
if (IsLeftToRight)
{
bool selectFromTheBeginning = selectionStartIndex <= previousCharacters;
int startIndex = selectFromTheBeginning ? 0 : Math.Abs(selectionStartIndex - previousCharacters);
int endIndex = Math.Abs(selectionEndIndex - previousCharacters);
int characters = Math.Min(endIndex - startIndex, currentLineLength - startIndex);
Vector2 selectedTextSize = Font.MeasureString(currentLine.Substring(startIndex, characters)) * TextBlock.TextScale;
Vector2 topLeft = selectFromTheBeginning
? new Vector2(offset.X, offset.Y + currentLineSize.Y * i)
: new Vector2(selectionStartPos.X, offset.Y + currentLineSize.Y * i);
GUI.DrawRectangle(spriteBatch, Rect.Location.ToVector2() + topLeft, selectedTextSize, SelectionColor, isFilled: true);
}
else
{
bool selectFromTheBeginning = selectionStartIndex >= totalIndex;
bool selectFromTheStart = selectionEndIndex <= previousCharacters;
int startIndex = selectFromTheBeginning ? currentLineLength : Math.Abs(selectionStartIndex - previousCharacters);
int endIndex = selectFromTheStart ? 0 : Math.Abs(selectionEndIndex - previousCharacters);
int characters = Math.Min(Math.Abs(endIndex - startIndex), currentLineLength);
Vector2 selectedTextSize = Font.MeasureString(currentLine.Substring(endIndex, characters)) * TextBlock.TextScale;
Vector2 topLeft = selectFromTheBeginning
? new Vector2(offset.X + currentLineSize.X - selectedTextSize.X, offset.Y + currentLineSize.Y * i)
: new Vector2(selectionStartPos.X - selectedTextSize.X, offset.Y + currentLineSize.Y * i);
GUI.DrawRectangle(spriteBatch, Rect.Location.ToVector2() + topLeft, selectedTextSize, SelectionColor, isFilled: true);
}
}
}
previousCharacters = totalIndex;
} }
Vector2 topLeft = characterPositions[startIndex];
for (int i = startIndex+1; i <= endIndex; i++)
{
Vector2 currPos = characterPositions[i];
if (!MathUtils.NearlyEqual(topLeft.Y, currPos.Y))
{
Vector2 bottomRight = characterPositions[i - 1];
bottomRight += Font.MeasureChar(Text[i - 1]);
drawRect(topLeft, bottomRight);
topLeft = currPos;
}
}
Vector2 finalBottomRight = characterPositions[endIndex];
finalBottomRight += Font.MeasureChar(Text[endIndex]);
drawRect(topLeft, finalBottomRight);
} }
else else
{ {
@@ -728,8 +696,15 @@ namespace Barotrauma
{ {
InitSelectionStart(); InitSelectionStart();
} }
float lineHeight = Font.MeasureString("T").Y * TextBlock.TextScale; float lineHeight = Font.LineHeight * TextBlock.TextScale;
int newIndex = textBlock.GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y - lineHeight)); int newIndex = textBlock.GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y - lineHeight * 0.5f));
textBlock.Font.WrapText(
textBlock.Text,
(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z) / TextBlock.TextScale,
newIndex,
out Vector2 requestedCharPos);
requestedCharPos *= TextBlock.TextScale;
if (MathUtils.NearlyEqual(requestedCharPos.Y, caretPos.Y)) { newIndex = 0; }
CaretIndex = newIndex; CaretIndex = newIndex;
caretTimer = 0; caretTimer = 0;
HandleSelection(); HandleSelection();
@@ -739,8 +714,15 @@ namespace Barotrauma
{ {
InitSelectionStart(); InitSelectionStart();
} }
lineHeight = Font.MeasureString("T").Y * TextBlock.TextScale; lineHeight = Font.LineHeight * TextBlock.TextScale;
newIndex = textBlock.GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y + lineHeight)); newIndex = textBlock.GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y + lineHeight * 1.5f));
textBlock.Font.WrapText(
textBlock.Text,
(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z) / TextBlock.TextScale,
newIndex,
out Vector2 requestedCharPos2);
requestedCharPos2 *= TextBlock.TextScale;
if (MathUtils.NearlyEqual(requestedCharPos2.Y, caretPos.Y)) { newIndex = Text.Length; }
CaretIndex = newIndex; CaretIndex = newIndex;
caretTimer = 0; caretTimer = 0;
HandleSelection(); HandleSelection();
@@ -803,6 +785,7 @@ namespace Barotrauma
} }
break; break;
} }
if (caretPosDirty) { CalculateCaretPos(); }
OnKeyHit?.Invoke(this, key); OnKeyHit?.Invoke(this, key);
void HandleSelection() void HandleSelection()
{ {
@@ -1,5 +1,6 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq; using System.Linq;
namespace Barotrauma namespace Barotrauma
@@ -37,39 +38,44 @@ namespace Barotrauma
values[0] = newValue; values[0] = newValue;
} }
public void Draw(SpriteBatch spriteBatch, Rectangle rect, float? maxVal, float xOffset, Color color) public delegate void GraphDelegate(SpriteBatch spriteBatch, float value, int order, Vector2 position);
public void Draw(SpriteBatch spriteBatch, Rectangle rect, float? maxValue = null, float xOffset = 0, Color? color = null, GraphDelegate doForEachValue = null)
{ {
color ??= Color.White;
float graphMaxVal = 1.0f; float graphMaxVal = 1.0f;
if (maxVal == null) if (maxValue == null)
{ {
graphMaxVal = LargestValue(); graphMaxVal = LargestValue();
} }
else if (maxVal > 0.0f) else if (maxValue > 0.0f)
{ {
graphMaxVal = (float)maxVal; graphMaxVal = (float)maxValue;
} }
GUI.DrawRectangle(spriteBatch, rect, Color.White); GUI.DrawRectangle(spriteBatch, rect, Color.White);
if (values.Length == 0) return; if (values.Length == 0) { return; }
float lineWidth = (float)rect.Width / (float)(values.Length - 2); float lineWidth = rect.Width / (float)(values.Length - 2);
float yScale = (float)rect.Height / graphMaxVal; float yScale = rect.Height / graphMaxVal;
Vector2 prevPoint = new Vector2(rect.Right, rect.Bottom - (values[1] + (values[0] - values[1]) * xOffset) * yScale); Vector2 prevPoint = new Vector2(rect.Right, rect.Bottom - (values[1] + (values[0] - values[1]) * xOffset) * yScale);
float currX = rect.Right - ((xOffset - 1.0f) * lineWidth); float currX = rect.Right - ((xOffset - 1.0f) * lineWidth);
for (int i = 1; i < values.Length - 1; i++) for (int i = 1; i < values.Length - 1; i++)
{ {
float value = values[i];
currX -= lineWidth; currX -= lineWidth;
Vector2 newPoint = new Vector2(currX, rect.Bottom - values[i] * yScale); Vector2 newPoint = new Vector2(currX, rect.Bottom - value * yScale);
GUI.DrawLine(spriteBatch, prevPoint, newPoint - new Vector2(1.0f, 0), color); GUI.DrawLine(spriteBatch, prevPoint, newPoint - new Vector2(1.0f, 0), color.Value);
prevPoint = newPoint; prevPoint = newPoint;
doForEachValue?.Invoke(spriteBatch, value, i, newPoint);
} }
int lastIndex = values.Length - 1;
Vector2 lastPoint = new Vector2(rect.X, float lastValue = values[lastIndex];
rect.Bottom - (values[values.Length - 1] + (values[values.Length - 2] - values[values.Length - 1]) * xOffset) * yScale); Vector2 lastPoint = new Vector2(rect.X, rect.Bottom - (lastValue + (values[values.Length - 2] - lastValue) * xOffset) * yScale);
GUI.DrawLine(spriteBatch, prevPoint, lastPoint, color.Value);
GUI.DrawLine(spriteBatch, prevPoint, lastPoint, color); doForEachValue?.Invoke(spriteBatch, lastValue, lastIndex, lastPoint);
} }
} }
} }
@@ -379,11 +379,42 @@ namespace Barotrauma
if (currSplashScreen.IsPlaying) if (currSplashScreen.IsPlaying)
{ {
graphics.Clear(Color.Black);
float videoAspectRatio = (float)currSplashScreen.Width / (float)currSplashScreen.Height;
int width; int height;
if (GameMain.GraphicsHeight * videoAspectRatio > GameMain.GraphicsWidth)
{
width = GameMain.GraphicsWidth;
height = (int)(GameMain.GraphicsWidth / videoAspectRatio);
}
else
{
width = (int)(GameMain.GraphicsHeight * videoAspectRatio);
height = GameMain.GraphicsHeight;
}
spriteBatch.Begin(); spriteBatch.Begin();
spriteBatch.Draw(currSplashScreen.GetTexture(), new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White); spriteBatch.Draw(
currSplashScreen.GetTexture(),
destinationRectangle: new Rectangle(
GameMain.GraphicsWidth / 2 - width / 2,
GameMain.GraphicsHeight / 2 - height / 2,
width,
height),
sourceRectangle: new Rectangle(0, 0, currSplashScreen.Width, currSplashScreen.Height),
Color.White,
rotation: 0.0f,
origin: Vector2.Zero,
SpriteEffects.None,
layerDepth: 0.0f);
spriteBatch.End(); spriteBatch.End();
if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 500) && GameMain.WindowActive && (PlayerInput.KeyHit(Keys.Escape) || PlayerInput.KeyHit(Keys.Space) || PlayerInput.KeyHit(Keys.Enter) || PlayerInput.PrimaryMouseButtonDown())) if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 500)
&& GameMain.WindowActive
&& (PlayerInput.KeyHit(Keys.Escape)
|| PlayerInput.KeyHit(Keys.Space)
|| PlayerInput.KeyHit(Keys.Enter)
|| PlayerInput.PrimaryMouseButtonDown()))
{ {
currSplashScreen.Dispose(); currSplashScreen = null; currSplashScreen.Dispose(); currSplashScreen = null;
} }
@@ -395,7 +426,7 @@ namespace Barotrauma
} }
bool drawn; bool drawn;
public IEnumerable<object> DoLoading(IEnumerable<object> loader) public IEnumerable<CoroutineStatus> DoLoading(IEnumerable<CoroutineStatus> loader)
{ {
drawn = false; drawn = false;
LoadState = null; LoadState = null;
@@ -732,7 +732,7 @@ namespace Barotrauma
CoroutineManager.StartCoroutine(DoScaleAnimation(targetSize, duration)); CoroutineManager.StartCoroutine(DoScaleAnimation(targetSize, duration));
} }
private IEnumerable<object> DoMoveAnimation(Point targetPos, float duration) private IEnumerable<CoroutineStatus> DoMoveAnimation(Point targetPos, float duration)
{ {
Vector2 startPos = AbsoluteOffset.ToVector2(); Vector2 startPos = AbsoluteOffset.ToVector2();
float t = 0.0f; float t = 0.0f;
@@ -746,7 +746,7 @@ namespace Barotrauma
animTargetPos = null; animTargetPos = null;
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
private IEnumerable<object> DoScaleAnimation(Point targetSize, float duration) private IEnumerable<CoroutineStatus> DoScaleAnimation(Point targetSize, float duration)
{ {
Vector2 startSize = NonScaledSize.ToVector2(); Vector2 startSize = NonScaledSize.ToVector2();
float t = 0.0f; float t = 0.0f;
@@ -151,9 +151,8 @@ namespace Barotrauma
/// </summary> /// </summary>
public static void DrawPoint(this SpriteBatch spriteBatch, Vector2 position, Color color, float size = 1f) public static void DrawPoint(this SpriteBatch spriteBatch, Vector2 position, Color color, float size = 1f)
{ {
var scale = Vector2.One * size;
var offset = new Vector2(0.5f) - new Vector2(size * 0.5f); var offset = new Vector2(0.5f) - new Vector2(size * 0.5f);
spriteBatch.Draw(GetTexture(spriteBatch), position + offset, null, color, 0.0f, Vector2.Zero, Vector2.One, SpriteEffects.None, 0); spriteBatch.Draw(GetTexture(spriteBatch), position + offset, null, color, 0.0f, Vector2.Zero, new Vector2(size), SpriteEffects.None, 0);
} }
public static void DrawCircle(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, Color color, public static void DrawCircle(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, Color color,
@@ -266,7 +266,7 @@ namespace Barotrauma
}; };
if (balanceAfterTransaction != CurrentLocation.StoreCurrentBalance) if (balanceAfterTransaction != CurrentLocation.StoreCurrentBalance)
{ {
var newStatus = Location.GetStoreBalanceStatus(balanceAfterTransaction); var newStatus = CurrentLocation.GetStoreBalanceStatus(balanceAfterTransaction);
if (CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier != newStatus.SellPriceModifier) if (CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier != newStatus.SellPriceModifier)
{ {
string tooltipTag = newStatus.SellPriceModifier > CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier ? string tooltipTag = newStatus.SellPriceModifier > CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier ?
@@ -471,7 +471,7 @@ namespace Barotrauma
} }
// Initial submarine selection needs a slight wait to allow the layoutgroups to place content properly // Initial submarine selection needs a slight wait to allow the layoutgroups to place content properly
private IEnumerable<object> SelectOwnSubmarineWithDelay(SubmarineInfo info, SubmarineDisplayContent display) private IEnumerable<CoroutineStatus> SelectOwnSubmarineWithDelay(SubmarineInfo info, SubmarineDisplayContent display)
{ {
yield return new WaitForSeconds(0.05f); yield return new WaitForSeconds(0.05f);
SelectSubmarine(info, display.background.Rect); SelectSubmarine(info, display.background.Rect);
@@ -1541,13 +1541,41 @@ namespace Barotrauma
GUITextBlock.AutoScaleAndNormalize(skillNames); GUITextBlock.AutoScaleAndNormalize(skillNames);
} }
private bool HasUnlockedAllTalents(Character controlledCharacter)
{
if (TalentTree.JobTalentTrees.TryGetValue(controlledCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree))
{
foreach (TalentSubTree talentSubTree in talentTree.TalentSubTrees)
{
foreach (TalentOption talentOption in talentSubTree.TalentOptionStages)
{
if (talentOption.Talents.None(t => controlledCharacter.HasTalent(t.Identifier)))
{
return false;
}
}
}
}
return true;
}
private void UpdateTalentButtons() private void UpdateTalentButtons()
{ {
Character controlledCharacter = Character.Controlled; Character controlledCharacter = Character.Controlled;
if (controlledCharacter?.Info == null) { return; }
experienceText.Text = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}"; bool unlockedAllTalents = HasUnlockedAllTalents(controlledCharacter);
experienceBar.BarSize = controlledCharacter.Info.GetProgressTowardsNextLevel();
//experienceBar.ToolTip = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}"; if (unlockedAllTalents)
{
experienceText.Text = string.Empty;
experienceBar.BarSize = 1f;
}
else
{
experienceText.Text = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}";
experienceBar.BarSize = controlledCharacter.Info.GetProgressTowardsNextLevel();
}
selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents); selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents);
@@ -1555,7 +1583,11 @@ namespace Barotrauma
int talentCount = selectedTalents.Count - controlledCharacter.Info.GetUnlockedTalentsInTree().Count(); int talentCount = selectedTalents.Count - controlledCharacter.Info.GetUnlockedTalentsInTree().Count();
if (talentCount > 0) if (unlockedAllTalents)
{
talentPointText.SetRichText($"‖color:{XMLExtensions.ToStringHex(Color.Gray)}‖{TextManager.Get("talentmenu.alltalentsunlocked")}‖color:end‖");
}
else if (talentCount > 0)
{ {
string pointsUsed = $"‖color:{XMLExtensions.ColorToString(GUI.Style.Red)}‖{-talentCount}‖color:end‖"; string pointsUsed = $"‖color:{XMLExtensions.ColorToString(GUI.Style.Red)}‖{-talentCount}‖color:end‖";
string localizedString = TextManager.GetWithVariables("talentmenu.points.spending", new []{ "[amount]", "[used]" }, new []{ pointsLeft, pointsUsed}); string localizedString = TextManager.GetWithVariables("talentmenu.points.spending", new []{ "[amount]", "[used]" }, new []{ pointsLeft, pointsUsed});
@@ -436,7 +436,7 @@ namespace Barotrauma
} }
} }
private IEnumerable<object> Load(bool isSeparateThread) private IEnumerable<CoroutineStatus> Load(bool isSeparateThread)
{ {
if (GameSettings.VerboseLogging) if (GameSettings.VerboseLogging)
{ {
@@ -534,9 +534,8 @@ namespace Barotrauma
Debug.WriteLine("sounds"); Debug.WriteLine("sounds");
int i = 0; int i = 0;
foreach (object crObj in SoundPlayer.Init()) foreach (CoroutineStatus status in SoundPlayer.Init())
{ {
CoroutineStatus status = (CoroutineStatus)crObj;
if (status == CoroutineStatus.Success) break; if (status == CoroutineStatus.Success) break;
i++; i++;
@@ -1000,11 +999,11 @@ namespace Barotrauma
} }
} }
NetworkMember?.Update((float)Timing.Step);
GUI.Update((float)Timing.Step); GUI.Update((float)Timing.Step);
} }
NetworkMember?.Update((float)Timing.Step);
CoroutineManager.Update((float)Timing.Step, Paused ? 0.0f : (float)Timing.Step); CoroutineManager.Update((float)Timing.Step, Paused ? 0.0f : (float)Timing.Step);
SteamManager.Update((float)Timing.Step); SteamManager.Update((float)Timing.Step);
@@ -1231,7 +1230,7 @@ namespace Barotrauma
} }
static bool waitForKeyHit = true; static bool waitForKeyHit = true;
public CoroutineHandle ShowLoading(IEnumerable<object> loader, bool waitKeyHit = true) public CoroutineHandle ShowLoading(IEnumerable<CoroutineStatus> loader, bool waitKeyHit = true)
{ {
waitForKeyHit = waitKeyHit; waitForKeyHit = waitKeyHit;
loadingScreenOpen = true; loadingScreenOpen = true;
@@ -1256,7 +1255,7 @@ namespace Barotrauma
} }
if (GameSettings.SendUserStatistics) { GameAnalytics.OnQuit(); } if (GameSettings.SendUserStatistics) { GameAnalytics.OnQuit(); }
if (GameSettings.SaveDebugConsoleLogs) { DebugConsole.SaveLogs(); } if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); }
base.OnExiting(sender, args); base.OnExiting(sender, args);
} }
@@ -40,12 +40,13 @@ namespace Barotrauma
private List<SoldEntity> SoldEntities { get; } = new List<SoldEntity>(); private List<SoldEntity> SoldEntities { get; } = new List<SoldEntity>();
// The bag slot is intentionally left out since we want to be able to sell items from there
private readonly List<InvSlotType> equipmentSlots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card };
public IEnumerable<Item> GetSellableItems(Character character) public IEnumerable<Item> GetSellableItems(Character character)
{ {
if (character == null) { return new List<Item>(); } if (character == null) { return new List<Item>(); }
var confirmedSoldEntities = GetConfirmedSoldEntities(); var confirmedSoldEntities = GetConfirmedSoldEntities();
// The bag slot is intentionally left out since we want to be able to sell items from there
var equipmentSlots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card };
return character.Inventory.FindAllItems(item => return character.Inventory.FindAllItems(item =>
{ {
if (!IsItemSellable(item, confirmedSoldEntities)) { return false; } if (!IsItemSellable(item, confirmedSoldEntities)) { return false; }
@@ -73,6 +74,7 @@ namespace Barotrauma
return Submarine.MainSub.GetItems(true).FindAll(item => return Submarine.MainSub.GetItems(true).FindAll(item =>
{ {
if (!IsItemSellable(item, confirmedSoldEntities)) { return false; } if (!IsItemSellable(item, confirmedSoldEntities)) { return false; }
if (item.GetRootInventoryOwner() is Character) { return false; }
if (!item.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { return false; } if (!item.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { return false; }
if (!item.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { return false; } if (!item.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { return false; }
if (!ItemAndAllContainersInteractable(item)) { return false; } if (!ItemAndAllContainersInteractable(item)) { return false; }
@@ -101,7 +103,7 @@ namespace Barotrauma
private bool IsItemSellable(Item item, IEnumerable<SoldEntity> confirmedSoldEntities) private bool IsItemSellable(Item item, IEnumerable<SoldEntity> confirmedSoldEntities)
{ {
if (!item.Prefab.CanBeSold) { return false; } if (!item.Prefab.CanBeSold) { return false; }
if (item.SpawnedInOutpost) { return false; } if (item.SpawnedInCurrentOutpost) { return false; }
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; } if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; } if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
if (item.OwnInventory?.Container is ItemContainer itemContainer) if (item.OwnInventory?.Container is ItemContainer itemContainer)
@@ -100,7 +100,7 @@ namespace Barotrauma
{ {
AutoHideScrollBar = false, AutoHideScrollBar = false,
CanBeFocused = false, CanBeFocused = false,
CanDragElements = true, CurrentDragMode = GUIListBox.DragMode.DragWithinBox,
CanInteractWhenUnfocusable = true, CanInteractWhenUnfocusable = true,
OnSelected = (component, userData) => false, OnSelected = (component, userData) => false,
SelectMultiple = false, SelectMultiple = false,
@@ -359,34 +359,41 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
var jobIconBackground = new GUIImage( // Hide the icon to make more space for the name if the crew list's width is small enough
bool isJobIconVisible = crewListEntrySize.X >= 220;
if (isJobIconVisible)
{
var jobIconBackground = new GUIImage(
new RectTransform(new Vector2(0.8f * iconRelativeWidth, 0.8f), layoutGroup.RectTransform), new RectTransform(new Vector2(0.8f * iconRelativeWidth, 0.8f), layoutGroup.RectTransform),
jobIndicatorBackground, jobIndicatorBackground,
scaleToFit: true) scaleToFit: true)
{
CanBeFocused = false,
UserData = "job"
};
if (character?.Info?.Job.Prefab?.Icon != null)
{
new GUIImage(
new RectTransform(Vector2.One, jobIconBackground.RectTransform),
character.Info.Job.Prefab.Icon,
scaleToFit: true)
{ {
CanBeFocused = false, CanBeFocused = false,
Color = character.Info.Job.Prefab.UIColor, UserData = "job"
HoverColor = character.Info.Job.Prefab.UIColor,
PressedColor = character.Info.Job.Prefab.UIColor,
SelectedColor = character.Info.Job.Prefab.UIColor
}; };
if (character?.Info?.Job.Prefab?.Icon != null)
{
new GUIImage(
new RectTransform(Vector2.One, jobIconBackground.RectTransform),
character.Info.Job.Prefab.Icon,
scaleToFit: true)
{
CanBeFocused = false,
Color = character.Info.Job.Prefab.UIColor,
HoverColor = character.Info.Job.Prefab.UIColor,
PressedColor = character.Info.Job.Prefab.UIColor,
SelectedColor = character.Info.Job.Prefab.UIColor
};
}
} }
int iconsVisible = isJobIconVisible ? 5 : 4;
var nameRelativeWidth = 1.0f var nameRelativeWidth = 1.0f
// Start padding // Start padding
- paddingRelativeWidth - paddingRelativeWidth
// 5 icons (job, 3 orders, sound) // icons (job, active orders, current task / voip)
- (5 * 0.8f * iconRelativeWidth) - (iconsVisible * 0.8f * iconRelativeWidth)
// Vertical line // Vertical line
- (0.1f * iconRelativeWidth) - (0.1f * iconRelativeWidth)
// Spacing // Spacing
@@ -425,7 +432,7 @@ namespace Barotrauma
var currentOrderList = new GUIListBox(new RectTransform(new Vector2(0.0f, 1.0f), parent: orderGroup.RectTransform), isHorizontal: true, style: null) var currentOrderList = new GUIListBox(new RectTransform(new Vector2(0.0f, 1.0f), parent: orderGroup.RectTransform), isHorizontal: true, style: null)
{ {
AllowMouseWheelScroll = false, AllowMouseWheelScroll = false,
CanDragElements = true, CurrentDragMode = GUIListBox.DragMode.DragWithinBox,
HideChildrenOutsideFrame = false, HideChildrenOutsideFrame = false,
KeepSpaceForScrollBar = false, KeepSpaceForScrollBar = false,
OnRearranged = OnOrdersRearranged, OnRearranged = OnOrdersRearranged,
@@ -439,7 +446,9 @@ namespace Barotrauma
if (component is GUIListBox list) if (component is GUIListBox list)
{ {
list.CanBeFocused = CanIssueOrders; list.CanBeFocused = CanIssueOrders;
list.CanDragElements = CanIssueOrders && list.Content.CountChildren > 1; list.CurrentDragMode = CanIssueOrders && list.Content.CountChildren > 1
? GUIListBox.DragMode.DragWithinBox
: GUIListBox.DragMode.NoDragging;
} }
}; };
@@ -507,8 +516,11 @@ namespace Barotrauma
{ {
if (!(characterComponent?.UserData is Character character)) { return; } if (!(characterComponent?.UserData is Character character)) { return; }
if (character.Info?.Job?.Prefab == null) { return; } if (character.Info?.Job?.Prefab == null) { return; }
string tooltip = TextManager.GetWithVariables("crewlistelementtooltip",
new string[] { "[name]", "[job]" },
new string[] { character.Name, character.Info.Job.Name });
string color = XMLExtensions.ColorToString(character.Info.Job.Prefab.UIColor); string color = XMLExtensions.ColorToString(character.Info.Job.Prefab.UIColor);
string tooltip = $"‖color:{color}‖{character.Name} ({character.Info.Job.Name})‖color:end‖"; tooltip = $"‖color:{color}‖{tooltip}‖color:end‖";
var richTextData = RichTextData.GetRichTextData(tooltip, out string sanitizedTooltip); var richTextData = RichTextData.GetRichTextData(tooltip, out string sanitizedTooltip);
characterComponent.ToolTip = sanitizedTooltip; characterComponent.ToolTip = sanitizedTooltip;
characterComponent.TooltipRichTextData = richTextData; characterComponent.TooltipRichTextData = richTextData;
@@ -546,7 +558,7 @@ namespace Barotrauma
RemoveCharacter(killedCharacter); RemoveCharacter(killedCharacter);
} }
private IEnumerable<object> KillCharacterAnim(GUIComponent component) private IEnumerable<CoroutineStatus> KillCharacterAnim(GUIComponent component)
{ {
List<GUIComponent> components = component.GetAllChildren().ToList(); List<GUIComponent> components = component.GetAllChildren().ToList();
components.Add(component); components.Add(component);
@@ -1648,7 +1660,7 @@ namespace Barotrauma
} }
if (characterComponent.Visible) if (characterComponent.Visible)
{ {
if (character == Character.Controlled && characterComponent.State != GUIComponent.ComponentState.Selected) if (character == Character.Controlled && crewList.SelectedComponent != characterComponent)
{ {
crewList.Select(character, force: true); crewList.Select(character, force: true);
} }
@@ -2637,7 +2649,7 @@ namespace Barotrauma
// If targeting a repairable item with condition below the repair threshold, show the 'repairsystems' order // If targeting a repairable item with condition below the repair threshold, show the 'repairsystems' order
orderIdentifier = "repairsystems"; orderIdentifier = "repairsystems";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Repairables.Any(r => itemContext.ConditionPercentage < r.RepairThreshold)) if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Repairables.Any(r => r.IsBelowRepairThreshold))
{ {
if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical")))) if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical"))))
{ {
@@ -2758,11 +2770,11 @@ namespace Barotrauma
if (AIObjectiveCleanupItems.IsValidTarget(item, Character.Controlled, checkInventory: false)) { return true; } if (AIObjectiveCleanupItems.IsValidTarget(item, Character.Controlled, checkInventory: false)) { return true; }
if (AIObjectiveCleanupItems.IsValidContainer(item, Character.Controlled)) { return true; } if (AIObjectiveCleanupItems.IsValidContainer(item, Character.Controlled)) { return true; }
if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold)) { return true; } if (item.Repairables.Any(r => r.IsBelowRepairThreshold)) { return true; }
var operateWeaponsPrefab = Order.GetPrefab("operateweapons"); var operateWeaponsPrefab = Order.GetPrefab("operateweapons");
return item.Components.Any(c => c is Controller) && return item.Components.Any(c => c is Controller) &&
(item.GetConnectedComponents<Turret>().Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)) || (item.GetConnectedComponents<Turret>().Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)) ||
item.GetConnectedComponents<Turret>(recursive: true).Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems))); item.GetConnectedComponents<Turret>(recursive: true).Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)));
} }
/// <param name="hotkey">Use a negative value (e.g. -1) if there should be no hotkey associated with the node</param> /// <param name="hotkey">Use a negative value (e.g. -1) if there should be no hotkey associated with the node</param>
@@ -2781,7 +2793,7 @@ namespace Barotrauma
disableNode = !CanCharacterBeHeard(); disableNode = !CanCharacterBeHeard();
} }
var mustSetOptionOrTarget = order.HasOptions; bool mustSetOptionOrTarget = order.HasOptions;
Item orderTargetEntity = null; Item orderTargetEntity = null;
// If the order doesn't have options, but must set a target, // If the order doesn't have options, but must set a target,
@@ -2804,14 +2816,14 @@ namespace Barotrauma
{ {
if (disableNode || !CanIssueOrders) { return false; } if (disableNode || !CanIssueOrders) { return false; }
var o = userData as Order; var o = userData as Order;
if (o.MustManuallyAssign && characterContext == null) if (mustSetOptionOrTarget)
{
CreateAssignmentNodes(node);
}
else if (mustSetOptionOrTarget)
{ {
NavigateForward(button, userData); NavigateForward(button, userData);
} }
else if (o.MustManuallyAssign && characterContext == null)
{
CreateAssignmentNodes(node);
}
else else
{ {
if (orderTargetEntity != null) if (orderTargetEntity != null)
@@ -2925,6 +2937,10 @@ namespace Barotrauma
{ {
NavigateForward(button, userData); NavigateForward(button, userData);
} }
else if (o.Item1.MustManuallyAssign && characterContext == null)
{
CreateAssignmentNodes(button);
}
else else
{ {
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, CharacterInfo.HighestManualOrderPriority, Character.Controlled); SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, CharacterInfo.HighestManualOrderPriority, Character.Controlled);
@@ -2987,12 +3003,19 @@ namespace Barotrauma
var node = new GUIButton(new RectTransform(size, parent: parent, anchor: Anchor.Center), style: null) var node = new GUIButton(new RectTransform(size, parent: parent, anchor: Anchor.Center), style: null)
{ {
UserData = new Tuple<Order, string>(order, option), UserData = new Tuple<Order, string>(order, option),
OnClicked = (_, userData) => OnClicked = (button, userData) =>
{ {
if (!CanIssueOrders) { return false; } if (!CanIssueOrders) { return false; }
var o = userData as Tuple<Order, string>; var o = userData as Tuple<Order, string>;
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, CharacterInfo.HighestManualOrderPriority, Character.Controlled); if (o.Item1.MustManuallyAssign && characterContext == null)
DisableCommandUI(); {
CreateAssignmentNodes(button);
}
else
{
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, CharacterInfo.HighestManualOrderPriority, Character.Controlled);
DisableCommandUI();
}
return true; return true;
} }
}; };
@@ -188,7 +188,7 @@ namespace Barotrauma
} }
private IEnumerable<object> DoInitialCameraTransition() private IEnumerable<CoroutineStatus> DoInitialCameraTransition()
{ {
while (GameMain.Instance.LoadingScreenOpen) while (GameMain.Instance.LoadingScreenOpen)
{ {
@@ -310,12 +310,12 @@ namespace Barotrauma
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
protected override IEnumerable<object> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults = null) protected override IEnumerable<CoroutineStatus> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults = null)
{ {
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
private IEnumerable<object> DoLevelTransition() private IEnumerable<CoroutineStatus> DoLevelTransition()
{ {
SoundPlayer.OverrideMusicType = CrewManager.GetCharacters().Any(c => !c.IsDead) ? "endround" : "crewdead"; SoundPlayer.OverrideMusicType = CrewManager.GetCharacters().Any(c => !c.IsDead) ? "endround" : "crewdead";
SoundPlayer.OverrideMusicDuration = 18.0f; SoundPlayer.OverrideMusicDuration = 18.0f;
@@ -361,7 +361,7 @@ namespace Barotrauma
//-------------------------------------- //--------------------------------------
//wait for the new level to be loaded //wait for the new level to be loaded
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, seconds: 30); DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, seconds: 60);
while (Level.Loaded == prevLevel || Level.Loaded == null) while (Level.Loaded == prevLevel || Level.Loaded == null)
{ {
if (DateTime.Now > timeOut || Screen.Selected != GameMain.GameScreen) { break; } if (DateTime.Now > timeOut || Screen.Selected != GameMain.GameScreen) { break; }
@@ -480,8 +480,6 @@ namespace Barotrauma
{ {
IsFirstRound = false; IsFirstRound = false;
CoroutineManager.StartCoroutine(DoLevelTransition(), "LevelTransition"); CoroutineManager.StartCoroutine(DoLevelTransition(), "LevelTransition");
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
GUI.SetSavingIndicatorState(success && (Level.IsLoadedOutpost || transitionType != TransitionType.None));
} }
} }
@@ -500,7 +498,7 @@ namespace Barotrauma
}; };
} }
private IEnumerable<object> DoEndCampaignCameraTransition() private IEnumerable<CoroutineStatus> DoEndCampaignCameraTransition()
{ {
Character controlled = Character.Controlled; Character controlled = Character.Controlled;
if (controlled != null) if (controlled != null)
@@ -243,7 +243,7 @@ namespace Barotrauma
mirror: map.CurrentLocation != map.SelectedConnection?.Locations[0])); mirror: map.CurrentLocation != map.SelectedConnection?.Locations[0]));
} }
private IEnumerable<object> DoLoadInitialLevel(LevelData level, bool mirror) private IEnumerable<CoroutineStatus> DoLoadInitialLevel(LevelData level, bool mirror)
{ {
GameMain.GameSession.StartRound(level, GameMain.GameSession.StartRound(level,
mirrorLevel: mirror); mirrorLevel: mirror);
@@ -254,7 +254,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
private IEnumerable<object> DoInitialCameraTransition() private IEnumerable<CoroutineStatus> DoInitialCameraTransition()
{ {
while (GameMain.Instance.LoadingScreenOpen) while (GameMain.Instance.LoadingScreenOpen)
{ {
@@ -378,7 +378,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
protected override IEnumerable<object> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults = null) protected override IEnumerable<CoroutineStatus> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults = null)
{ {
NextLevel = newLevel; NextLevel = newLevel;
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead); bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
@@ -515,7 +515,7 @@ namespace Barotrauma
}; };
} }
private IEnumerable<object> DoEndCampaignCameraTransition() private IEnumerable<CoroutineStatus> DoEndCampaignCameraTransition()
{ {
if (Character.Controlled != null) if (Character.Controlled != null)
{ {
@@ -15,7 +15,7 @@ namespace Barotrauma.Tutorials
{ {
} }
public override IEnumerable<object> UpdateState() public override IEnumerable<CoroutineStatus> UpdateState()
{ {
Character Controlled = Character.Controlled; Character Controlled = Character.Controlled;
if (Controlled == null) yield return CoroutineStatus.Success; if (Controlled == null) yield return CoroutineStatus.Success;
@@ -634,7 +634,7 @@ namespace Barotrauma.Tutorials
return Character.Controlled.Inventory.FindItemByIdentifier(itemIdentifier) != null; return Character.Controlled.Inventory.FindItemByIdentifier(itemIdentifier) != null;
} }
protected IEnumerable<object> KeepReactorRunning(Reactor reactor) protected IEnumerable<CoroutineStatus> KeepReactorRunning(Reactor reactor)
{ {
do do
{ {
@@ -652,7 +652,7 @@ namespace Barotrauma.Tutorials
/// <summary> /// <summary>
/// keeps the enemy away from the sub until the capacitors are loaded /// keeps the enemy away from the sub until the capacitors are loaded
/// </summary> /// </summary>
private IEnumerable<object> KeepEnemyAway(Character enemy, PowerContainer[] capacitors) private IEnumerable<CoroutineStatus> KeepEnemyAway(Character enemy, PowerContainer[] capacitors)
{ {
do do
{ {
@@ -141,7 +141,7 @@ namespace Barotrauma.Tutorials
captain_mechanic.AIController.Enabled = captain_security.AIController.Enabled = captain_engineer.AIController.Enabled = false; captain_mechanic.AIController.Enabled = captain_security.AIController.Enabled = captain_engineer.AIController.Enabled = false;
} }
public override IEnumerable<object> UpdateState() public override IEnumerable<CoroutineStatus> UpdateState()
{ {
while (GameMain.Instance.LoadingScreenOpen) yield return null; while (GameMain.Instance.LoadingScreenOpen) yield return null;
@@ -139,7 +139,7 @@ namespace Barotrauma.Tutorials
reactorItem.GetComponent<Reactor>().AutoTemp = true; reactorItem.GetComponent<Reactor>().AutoTemp = true;
} }
public override IEnumerable<object> UpdateState() public override IEnumerable<CoroutineStatus> UpdateState()
{ {
while (GameMain.Instance.LoadingScreenOpen) yield return null; while (GameMain.Instance.LoadingScreenOpen) yield return null;
@@ -446,7 +446,7 @@ namespace Barotrauma.Tutorials
CoroutineManager.StartCoroutine(TutorialCompleted()); CoroutineManager.StartCoroutine(TutorialCompleted());
} }
public IEnumerable<object> KeepPatientAlive(Character patient) public IEnumerable<CoroutineStatus> KeepPatientAlive(Character patient)
{ {
while (patient != null && !patient.Removed) while (patient != null && !patient.Removed)
{ {
@@ -10,7 +10,7 @@ namespace Barotrauma.Tutorials
{ {
} }
public override IEnumerable<object> UpdateState() public override IEnumerable<CoroutineStatus> UpdateState()
{ {
/*infoBox = CreateInfoFrame("Use the mouse wheel to zoom in and out, and WASD to move the camera around.", true); /*infoBox = CreateInfoFrame("Use the mouse wheel to zoom in and out, and WASD to move the camera around.", true);
@@ -206,7 +206,7 @@ namespace Barotrauma.Tutorials
engineer_submarineJunctionBox_3.Condition = 0f; engineer_submarineJunctionBox_3.Condition = 0f;
} }
public override IEnumerable<object> UpdateState() public override IEnumerable<CoroutineStatus> UpdateState()
{ {
while (GameMain.Instance.LoadingScreenOpen) yield return null; while (GameMain.Instance.LoadingScreenOpen) yield return null;
@@ -378,7 +378,7 @@ namespace Barotrauma.Tutorials
} }
} }
yield return null; yield return null;
} while (engineer_brokenJunctionBox.Condition < repairableJunctionBoxComponent.RepairThreshold); // Wait until repaired } while (repairableJunctionBoxComponent.IsBelowRepairThreshold); // Wait until repaired
SetHighlight(engineer_brokenJunctionBox, false); SetHighlight(engineer_brokenJunctionBox, false);
RemoveCompletedObjective(segments[3]); RemoveCompletedObjective(segments[3]);
SetDoorAccess(engineer_thirdDoor, engineer_thirdDoorLight, true); SetDoorAccess(engineer_thirdDoor, engineer_thirdDoorLight, true);
@@ -422,7 +422,7 @@ namespace Barotrauma.Tutorials
Repairable repairableJunctionBoxComponent3 = engineer_submarineJunctionBox_3.GetComponent<Repairable>(); Repairable repairableJunctionBoxComponent3 = engineer_submarineJunctionBox_3.GetComponent<Repairable>();
// Remove highlights when each individual machine is repaired // Remove highlights when each individual machine is repaired
do { CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3); yield return null; } while (engineer_submarineJunctionBox_1.Condition < repairableJunctionBoxComponent1.RepairThreshold || engineer_submarineJunctionBox_2.Condition < repairableJunctionBoxComponent2.RepairThreshold || engineer_submarineJunctionBox_3.Condition < repairableJunctionBoxComponent3.RepairThreshold); do { CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3); yield return null; } while (repairableJunctionBoxComponent1.IsBelowRepairThreshold || repairableJunctionBoxComponent2.IsBelowRepairThreshold || repairableJunctionBoxComponent3.IsBelowRepairThreshold);
CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3); CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3);
RemoveCompletedObjective(segments[5]); RemoveCompletedObjective(segments[5]);
yield return new WaitForSeconds(2f, false); yield return new WaitForSeconds(2f, false);
@@ -462,7 +462,7 @@ namespace Barotrauma.Tutorials
return engineer?.SelectedConstruction == item; return engineer?.SelectedConstruction == item;
} }
private IEnumerable<object> ReactorOperatedProperly() private IEnumerable<CoroutineStatus> ReactorOperatedProperly()
{ {
float timer; float timer;
@@ -566,17 +566,17 @@ namespace Barotrauma.Tutorials
private void CheckJunctionBoxHighlights(Repairable comp1, Repairable comp2, Repairable comp3) private void CheckJunctionBoxHighlights(Repairable comp1, Repairable comp2, Repairable comp3)
{ {
if (engineer_submarineJunctionBox_1.Condition > comp1.RepairThreshold && engineer_submarineJunctionBox_1.ExternalHighlight) if (!comp1.IsBelowRepairThreshold && engineer_submarineJunctionBox_1.ExternalHighlight)
{ {
SetHighlight(engineer_submarineJunctionBox_1, false); SetHighlight(engineer_submarineJunctionBox_1, false);
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_1); engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_1);
} }
if (engineer_submarineJunctionBox_2.Condition > comp2.RepairThreshold && engineer_submarineJunctionBox_2.ExternalHighlight) if (!comp2.IsBelowRepairThreshold && engineer_submarineJunctionBox_2.ExternalHighlight)
{ {
SetHighlight(engineer_submarineJunctionBox_2, false); SetHighlight(engineer_submarineJunctionBox_2, false);
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_2); engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_2);
} }
if (engineer_submarineJunctionBox_3.Condition > comp3.RepairThreshold && engineer_submarineJunctionBox_3.ExternalHighlight) if (!comp3.IsBelowRepairThreshold && engineer_submarineJunctionBox_3.ExternalHighlight)
{ {
SetHighlight(engineer_submarineJunctionBox_3, false); SetHighlight(engineer_submarineJunctionBox_3, false);
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_3); engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_3);
@@ -225,7 +225,7 @@ namespace Barotrauma.Tutorials
base.Update(deltaTime); base.Update(deltaTime);
} }
public override IEnumerable<object> UpdateState() public override IEnumerable<CoroutineStatus> UpdateState()
{ {
while (GameMain.Instance.LoadingScreenOpen) yield return null; while (GameMain.Instance.LoadingScreenOpen) yield return null;
@@ -550,7 +550,7 @@ namespace Barotrauma.Tutorials
do do
{ {
yield return null; yield return null;
if (mechanic_brokenPump.Item.Condition < repairablePumpComponent.RepairThreshold) if (repairablePumpComponent.IsBelowRepairThreshold)
{ {
if (!mechanic.HasEquippedItem("wrench")) if (!mechanic.HasEquippedItem("wrench"))
{ {
@@ -574,7 +574,7 @@ namespace Barotrauma.Tutorials
} }
} }
} }
} while (mechanic_brokenPump.Item.Condition < repairablePumpComponent.RepairThreshold || mechanic_brokenPump.FlowPercentage >= 0 || !mechanic_brokenPump.IsActive); } while (repairablePumpComponent.IsBelowRepairThreshold || mechanic_brokenPump.FlowPercentage >= 0 || !mechanic_brokenPump.IsActive);
RemoveCompletedObjective(segments[9]); RemoveCompletedObjective(segments[9]);
SetHighlight(mechanic_brokenPump.Item, false); SetHighlight(mechanic_brokenPump.Item, false);
do { yield return null; } while (mechanic_brokenhull_2.WaterPercentage > waterVolumeBeforeOpening); do { yield return null; } while (mechanic_brokenhull_2.WaterPercentage > waterVolumeBeforeOpening);
@@ -597,7 +597,7 @@ namespace Barotrauma.Tutorials
Repairable repairableEngineComponent = mechanic_submarineEngine.Item.GetComponent<Repairable>(); Repairable repairableEngineComponent = mechanic_submarineEngine.Item.GetComponent<Repairable>();
// Remove highlights when each individual machine is repaired // Remove highlights when each individual machine is repaired
do { CheckHighlights(repairablePumpComponent1, repairablePumpComponent2, repairableEngineComponent); yield return null; } while (mechanic_ballastPump_1.Item.Condition < repairablePumpComponent1.RepairThreshold || mechanic_ballastPump_2.Item.Condition < repairablePumpComponent2.RepairThreshold || mechanic_submarineEngine.Item.Condition < repairableEngineComponent.RepairThreshold); do { CheckHighlights(repairablePumpComponent1, repairablePumpComponent2, repairableEngineComponent); yield return null; } while (repairablePumpComponent1.IsBelowRepairThreshold || repairablePumpComponent2.IsBelowRepairThreshold || repairableEngineComponent.IsBelowRepairThreshold);
CheckHighlights(repairablePumpComponent1, repairablePumpComponent2, repairableEngineComponent); CheckHighlights(repairablePumpComponent1, repairablePumpComponent2, repairableEngineComponent);
RemoveCompletedObjective(segments[10]); RemoveCompletedObjective(segments[10]);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Complete"), ChatMessageType.Radio, null); GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Complete"), ChatMessageType.Radio, null);
@@ -623,17 +623,17 @@ namespace Barotrauma.Tutorials
private void CheckHighlights(Repairable comp1, Repairable comp2, Repairable comp3) private void CheckHighlights(Repairable comp1, Repairable comp2, Repairable comp3)
{ {
if (mechanic_ballastPump_1.Item.Condition > comp1.RepairThreshold && mechanic_ballastPump_1.Item.ExternalHighlight) if (!comp1.IsBelowRepairThreshold && mechanic_ballastPump_1.Item.ExternalHighlight)
{ {
SetHighlight(mechanic_ballastPump_1.Item, false); SetHighlight(mechanic_ballastPump_1.Item, false);
mechanic.RemoveActiveObjectiveEntity(mechanic_ballastPump_1.Item); mechanic.RemoveActiveObjectiveEntity(mechanic_ballastPump_1.Item);
} }
if (mechanic_ballastPump_2.Item.Condition > comp2.RepairThreshold && mechanic_ballastPump_2.Item.ExternalHighlight) if (!comp2.IsBelowRepairThreshold && mechanic_ballastPump_2.Item.ExternalHighlight)
{ {
SetHighlight(mechanic_ballastPump_2.Item, false); SetHighlight(mechanic_ballastPump_2.Item, false);
mechanic.RemoveActiveObjectiveEntity(mechanic_ballastPump_2.Item); mechanic.RemoveActiveObjectiveEntity(mechanic_ballastPump_2.Item);
} }
if (mechanic_submarineEngine.Item.Condition > comp3.RepairThreshold && mechanic_submarineEngine.Item.ExternalHighlight) if (!comp3.IsBelowRepairThreshold && mechanic_submarineEngine.Item.ExternalHighlight)
{ {
SetHighlight(mechanic_submarineEngine.Item, false); SetHighlight(mechanic_submarineEngine.Item, false);
mechanic.RemoveActiveObjectiveEntity(mechanic_submarineEngine.Item); mechanic.RemoveActiveObjectiveEntity(mechanic_submarineEngine.Item);
@@ -201,7 +201,7 @@ namespace Barotrauma.Tutorials
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, true); SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, true);
} }
public override IEnumerable<object> UpdateState() public override IEnumerable<CoroutineStatus> UpdateState()
{ {
while (GameMain.Instance.LoadingScreenOpen) yield return null; while (GameMain.Instance.LoadingScreenOpen) yield return null;
@@ -54,7 +54,7 @@ namespace Barotrauma.Tutorials
GameMain.Instance.ShowLoading(Loading()); GameMain.Instance.ShowLoading(Loading());
} }
private IEnumerable<object> Loading() private IEnumerable<CoroutineStatus> Loading()
{ {
SubmarineInfo subInfo = new SubmarineInfo(submarinePath); SubmarineInfo subInfo = new SubmarineInfo(submarinePath);
@@ -259,7 +259,7 @@ namespace Barotrauma.Tutorials
base.Stop(); base.Stop();
} }
private IEnumerable<object> Dead() private IEnumerable<CoroutineStatus> Dead()
{ {
GUI.PreventPauseMenuToggle = true; GUI.PreventPauseMenuToggle = true;
Character.Controlled = character = null; Character.Controlled = character = null;
@@ -279,7 +279,7 @@ namespace Barotrauma.Tutorials
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
protected IEnumerable<object> TutorialCompleted() protected IEnumerable<CoroutineStatus> TutorialCompleted()
{ {
GUI.PreventPauseMenuToggle = true; GUI.PreventPauseMenuToggle = true;
@@ -247,7 +247,7 @@ namespace Barotrauma.Tutorials
} }
} }
public virtual IEnumerable<object> UpdateState() public virtual IEnumerable<CoroutineStatus> UpdateState()
{ {
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
@@ -470,7 +470,7 @@ namespace Barotrauma.Tutorials
CoroutineManager.StartCoroutine(WaitForObjectiveEnd(segment)); CoroutineManager.StartCoroutine(WaitForObjectiveEnd(segment));
} }
private IEnumerable<object> WaitForObjectiveEnd(TutorialSegment objective) private IEnumerable<CoroutineStatus> WaitForObjectiveEnd(TutorialSegment objective)
{ {
yield return new WaitForSeconds(2.0f); yield return new WaitForSeconds(2.0f);
objectiveFrame.RemoveChild(objective.ReplayButton); objectiveFrame.RemoveChild(objective.ReplayButton);
@@ -24,7 +24,7 @@ namespace Barotrauma
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
//don't consider the items to belong in the outpost to prevent the stealing icon from showing //don't consider the items to belong in the outpost to prevent the stealing icon from showing
item.SpawnedInOutpost = false; item.SpawnedInCurrentOutpost = false;
} }
} }
@@ -106,7 +106,8 @@ namespace Barotrauma
respawnButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), respawnInfoFrame.RectTransform, Anchor.CenterRight), isHorizontal: true, childAnchor: Anchor.CenterLeft) respawnButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), respawnInfoFrame.RectTransform, Anchor.CenterRight), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{ {
AbsoluteSpacing = HUDLayoutSettings.Padding, AbsoluteSpacing = HUDLayoutSettings.Padding,
Stretch = true Stretch = true,
Visible = false
}; };
respawnTickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, respawnButtonContainer.RectTransform, Anchor.Center), TextManager.Get("respawnquestionpromptrespawn")) respawnTickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, respawnButtonContainer.RectTransform, Anchor.Center), TextManager.Get("respawnquestionpromptrespawn"))
{ {
@@ -133,7 +133,7 @@ namespace Barotrauma
string hintIdentifierBase = "onstartedinteracting"; string hintIdentifierBase = "onstartedinteracting";
// onstartedinteracting.brokenitem // onstartedinteracting.brokenitem
if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold)) if (item.Repairables.Any(r => r.IsBelowRepairThreshold))
{ {
if (DisplayHint($"{hintIdentifierBase}.brokenitem")) { return; } if (DisplayHint($"{hintIdentifierBase}.brokenitem")) { return; }
} }
@@ -192,7 +192,7 @@ namespace Barotrauma
if (!CanDisplayHints(requireGameScreen: false, requireControllingCharacter: false)) { return; } if (!CanDisplayHints(requireGameScreen: false, requireControllingCharacter: false)) { return; }
CoroutineManager.StartCoroutine(DisplayRoundStartedHints(initRoundHandle), "HintManager.DisplayRoundStartedHints"); CoroutineManager.StartCoroutine(DisplayRoundStartedHints(initRoundHandle), "HintManager.DisplayRoundStartedHints");
static IEnumerable<object> InitRound() static IEnumerable<CoroutineStatus> InitRound()
{ {
while (Character.Controlled == null) { yield return CoroutineStatus.Running; } while (Character.Controlled == null) { yield return CoroutineStatus.Running; }
// Get the ballast hulls on round start not to find them again and again later // Get the ballast hulls on round start not to find them again and again later
@@ -211,7 +211,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
static IEnumerable<object> DisplayRoundStartedHints(CoroutineHandle initRoundHandle) static IEnumerable<CoroutineStatus> DisplayRoundStartedHints(CoroutineHandle initRoundHandle)
{ {
while (GameMain.Instance.LoadingScreenOpen || Screen.Selected != GameMain.GameScreen || while (GameMain.Instance.LoadingScreenOpen || Screen.Selected != GameMain.GameScreen ||
CoroutineManager.IsCoroutineRunning(initRoundHandle) || CoroutineManager.IsCoroutineRunning(initRoundHandle) ||
@@ -480,7 +480,7 @@ namespace Barotrauma
"\n" + string.Join("\n", contentPackage.ErrorMessages); "\n" + string.Join("\n", contentPackage.ErrorMessages);
} }
} }
contentPackageList.CanDragElements = CanHotswapPackages(false); contentPackageList.CurrentDragMode = CanHotswapPackages(false) ? GUIListBox.DragMode.DragWithinBox : GUIListBox.DragMode.NoDragging;
contentPackageList.CanBeFocused = CanHotswapPackages(false); contentPackageList.CanBeFocused = CanHotswapPackages(false);
contentPackageList.OnRearranged = OnContentPackagesRearranged; contentPackageList.OnRearranged = OnContentPackagesRearranged;
@@ -1767,7 +1767,7 @@ namespace Barotrauma
return true; return true;
} }
private IEnumerable<object> WaitForKeyPress(GUITextBox keyBox, KeyOrMouse[] keyArray) private IEnumerable<CoroutineStatus> WaitForKeyPress(GUITextBox keyBox, KeyOrMouse[] keyArray)
{ {
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -949,16 +949,17 @@ namespace Barotrauma
//player has selected the inventory of another item -> attempt to move the item there //player has selected the inventory of another item -> attempt to move the item there
return QuickUseAction.PutToContainer; return QuickUseAction.PutToContainer;
} }
else if (character.SelectedCharacter != null && else if (character.SelectedCharacter?.Inventory != null &&
character.SelectedCharacter.Inventory != null &&
!character.SelectedCharacter.Inventory.Locked && !character.SelectedCharacter.Inventory.Locked &&
allowInventorySwap) allowInventorySwap)
{ {
//player has selected the inventory of another character -> attempt to move the item there //player has selected the inventory of another character -> attempt to move the item there
return QuickUseAction.PutToCharacter; return QuickUseAction.PutToCharacter;
} }
else if (character.SelectedBy != null && Character.Controlled == character.SelectedBy && else if (character.SelectedBy?.Inventory != null &&
character.SelectedBy.Inventory != null && !character.SelectedBy.Inventory.Locked && allowInventorySwap) Character.Controlled == character.SelectedBy &&
!character.SelectedBy.Inventory.Locked &&
allowInventorySwap)
{ {
return QuickUseAction.TakeFromCharacter; return QuickUseAction.TakeFromCharacter;
} }
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
float sizeMultiplier = Math.Clamp(chargeRatio, 0.1f, 1f); float sizeMultiplier = Math.Clamp(chargeRatio, 0.1f, 1f);
foreach (ParticleEmitter emitter in particleEmitterCharges) foreach (ParticleEmitter emitter in particleEmitterCharges)
{ {
emitter.Emit(deltaTime, particlePos, hullGuess: null, sizeMultiplier: sizeMultiplier, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier); emitter.Emit(deltaTime, particlePos, hullGuess: item.CurrentHull, sizeMultiplier: sizeMultiplier, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier);
} }
if (chargeSoundChannel == null || !chargeSoundChannel.IsPlaying) if (chargeSoundChannel == null || !chargeSoundChannel.IsPlaying)
@@ -157,6 +157,14 @@ namespace Barotrauma.Items.Components
crosshairSprite?.Draw(spriteBatch, crosshairPos, Color.White, 0, currentCrossHairScale); crosshairSprite?.Draw(spriteBatch, crosshairPos, Color.White, 0, currentCrossHairScale);
crosshairPointerSprite?.Draw(spriteBatch, crosshairPointerPos, 0, currentCrossHairPointerScale); crosshairPointerSprite?.Draw(spriteBatch, crosshairPointerPos, 0, currentCrossHairPointerScale);
} }
if (GameMain.DebugDraw)
{
Vector2 barrelPos = item.DrawPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);
barrelPos = Screen.Selected.Cam.WorldToScreen(barrelPos);
GUI.DrawLine(spriteBatch, barrelPos - Vector2.UnitY * 3, barrelPos + Vector2.UnitY * 3, Color.Red);
GUI.DrawLine(spriteBatch, barrelPos - Vector2.UnitX * 3, barrelPos + Vector2.UnitX * 3, Color.Red);
}
} }
partial void LaunchProjSpecific() partial void LaunchProjSpecific()
@@ -166,7 +174,7 @@ namespace Barotrauma.Items.Components
if (item.body.Dir < 0.0f) { rotation += MathHelper.Pi; } if (item.body.Dir < 0.0f) { rotation += MathHelper.Pi; }
foreach (ParticleEmitter emitter in particleEmitters) foreach (ParticleEmitter emitter in particleEmitters)
{ {
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: rotation, particleRotation: rotation); emitter.Emit(1.0f, particlePos, hullGuess: item.CurrentHull, angle: rotation, particleRotation: rotation);
} }
} }
@@ -576,7 +576,7 @@ namespace Barotrauma.Items.Components
delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(type, buffer, sendingTime, waitForMidRoundSync)); delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(type, buffer, sendingTime, waitForMidRoundSync));
} }
private IEnumerable<object> DoDelayedCorrection(ServerNetObject type, IReadMessage buffer, float sendingTime, bool waitForMidRoundSync) private IEnumerable<CoroutineStatus> DoDelayedCorrection(ServerNetObject type, IReadMessage buffer, float sendingTime, bool waitForMidRoundSync)
{ {
while (GameMain.Client != null && while (GameMain.Client != null &&
(correctionTimer > 0.0f || (waitForMidRoundSync && GameMain.Client.MidRoundSyncing))) (correctionTimer > 0.0f || (waitForMidRoundSync && GameMain.Client.MidRoundSyncing)))
@@ -14,6 +14,8 @@ namespace Barotrauma.Items.Components
private CoroutineHandle resetPredictionCoroutine; private CoroutineHandle resetPredictionCoroutine;
private float resetPredictionTimer; private float resetPredictionTimer;
private float currentBrightness;
public Vector2 DrawSize public Vector2 DrawSize
{ {
get { return new Vector2(Light.Range * 2, Light.Range * 2); } get { return new Vector2(Light.Range * 2, Light.Range * 2); }
@@ -31,12 +33,40 @@ namespace Barotrauma.Items.Components
{ {
if (Light == null) { return; } if (Light == null) { return; }
Light.Enabled = enabled; Light.Enabled = enabled;
currentBrightness = brightness;
if (enabled) if (enabled)
{ {
Light.Color = LightColor.Multiply(brightness); Light.Color = LightColor.Multiply(brightness);
} }
} }
partial void SetLightSourceTransform()
{
if (ParentBody != null)
{
Light.Position = ParentBody.Position;
}
else if (turret != null)
{
Light.Position = new Vector2(item.Rect.X + turret.TransformedBarrelPos.X, item.Rect.Y - turret.TransformedBarrelPos.Y);
}
else
{
Light.Position = item.Position;
}
PhysicsBody body = ParentBody ?? item.body;
if (body != null)
{
Light.Rotation = body.Dir > 0.0f ? body.DrawRotation : body.DrawRotation - MathHelper.Pi;
Light.LightSpriteEffect = (body.Dir > 0.0f) ? SpriteEffects.None : SpriteEffects.FlipVertically;
}
else
{
Light.Rotation = -Rotation - MathHelper.ToRadians(item.Rotation);
Light.LightSpriteEffect = item.SpriteEffects;
}
}
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{ {
if (Light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn && Light.Enabled) if (Light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn && Light.Enabled)
@@ -71,7 +101,7 @@ namespace Barotrauma.Items.Components
/// <summary> /// <summary>
/// Reset client-side prediction of the light's state to the last known state sent by the server after resetPredictionTimer runs out /// Reset client-side prediction of the light's state to the last known state sent by the server after resetPredictionTimer runs out
/// </summary> /// </summary>
private IEnumerable<object> ResetPredictionAfterDelay() private IEnumerable<CoroutineStatus> ResetPredictionAfterDelay()
{ {
while (resetPredictionTimer > 0.0f) while (resetPredictionTimer > 0.0f)
{ {
@@ -259,7 +259,9 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
SetActive(msg.ReadBoolean()); ushort userID = msg.ReadUInt16();
Character user = userID == Entity.NullEntityID ? null : Entity.FindEntityByID(userID) as Character;
SetActive(msg.ReadBoolean(), user);
progressTimer = msg.ReadSingle(); progressTimer = msg.ReadSingle();
} }
} }
@@ -329,8 +329,6 @@ namespace Barotrauma.Items.Components
var missingCounts = missingItems.GroupBy(missingItem => missingItem).ToDictionary(x => x.Key, x => x.Count()); var missingCounts = missingItems.GroupBy(missingItem => missingItem).ToDictionary(x => x.Key, x => x.Count());
missingItems = missingItems.Distinct().ToList(); missingItems = missingItems.Distinct().ToList();
var availableIngredients = GetAvailableIngredients();
foreach (FabricationRecipe.RequiredItem requiredItem in missingItems) foreach (FabricationRecipe.RequiredItem requiredItem in missingItems)
{ {
while (slotIndex < inputContainer.Capacity && inputContainer.Inventory.GetItemAt(slotIndex) != null) while (slotIndex < inputContainer.Capacity && inputContainer.Inventory.GetItemAt(slotIndex) != null)
@@ -341,23 +339,23 @@ namespace Barotrauma.Items.Components
requiredItem.ItemPrefabs requiredItem.ItemPrefabs
.Where(requiredPrefab => availableIngredients.ContainsKey(requiredPrefab.Identifier)) .Where(requiredPrefab => availableIngredients.ContainsKey(requiredPrefab.Identifier))
.ForEach(requiredPrefab => { .ForEach(requiredPrefab => {
var availablePrefabs = availableIngredients[requiredPrefab.Identifier]; var availableItems = availableIngredients[requiredPrefab.Identifier];
foreach (Item it in availableItems)
availablePrefabs {
.Where(availablePrefab => availablePrefab.ParentInventory != inputContainer.Inventory) if (it.ParentInventory == inputContainer.Inventory) { continue; }
.Where(availablePrefab => availablePrefab.ParentInventory.visualSlots != null) //slots are null if the inventory has never been displayed var rootContainer = it.GetRootContainer();
.ForEach(availablePrefab => { //(linked item, but the UI is not set to be displayed at the same time) if (rootContainer?.OwnInventory?.visualSlots == null) { continue; }
int availableSlotIndex = availablePrefab.ParentInventory.FindIndex(availablePrefab); int availableSlotIndex = rootContainer.OwnInventory.FindIndex(it.Container == rootContainer ? it : it.Container);
if (availableSlotIndex < 0) { continue; }
if (availablePrefab.ParentInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f) if (rootContainer.OwnInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
{
rootContainer.OwnInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
if (slotIndex < inputContainer.Capacity)
{ {
availablePrefab.ParentInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f); inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
if (slotIndex < inputContainer.Capacity)
{
inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
}
} }
}); }
}
}); });
if (slotIndex >= inputContainer.Capacity) { break; } if (slotIndex >= inputContainer.Capacity) { break; }
@@ -676,7 +674,17 @@ namespace Barotrauma.Items.Components
activateButton.Enabled = false; activateButton.Enabled = false;
inSufficientPowerWarning.Visible = currPowerConsumption > 0 && !hasPower; inSufficientPowerWarning.Visible = currPowerConsumption > 0 && !hasPower;
var availableIngredients = GetAvailableIngredients(); if (!IsActive)
{
//only check ingredients if the fabricator isn't active (if it is, this is done in Update)
if (refreshIngredientsTimer <= 0.0f)
{
RefreshAvailableIngredients();
refreshIngredientsTimer = RefreshIngredientsInterval;
}
refreshIngredientsTimer -= deltaTime;
}
if (character != null) if (character != null)
{ {
foreach (GUIComponent child in itemList.Content.Children) foreach (GUIComponent child in itemList.Content.Children)
@@ -183,6 +183,7 @@ namespace Barotrauma.Items.Components
private ImmutableDictionary<MapEntity, MiniMapGUIComponent> electricalMapComponents; private ImmutableDictionary<MapEntity, MiniMapGUIComponent> electricalMapComponents;
private ImmutableDictionary<MiniMapGUIComponent, GUIComponent> electricalChildren; private ImmutableDictionary<MiniMapGUIComponent, GUIComponent> electricalChildren;
private ImmutableDictionary<MiniMapGUIComponent, GUIComponent> doorChildren; private ImmutableDictionary<MiniMapGUIComponent, GUIComponent> doorChildren;
private ImmutableDictionary<MiniMapGUIComponent, GUIComponent> weaponChildren;
private ImmutableHashSet<ItemPrefab>? itemsFoundOnSub; private ImmutableHashSet<ItemPrefab>? itemsFoundOnSub;
@@ -366,8 +367,8 @@ namespace Barotrauma.Items.Components
hullInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.13f), GUI.Canvas, minSize: new Point(250, 150)), style: "GUIToolTip") hullInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.13f), GUI.Canvas, minSize: new Point(250, 150)), style: "GUIToolTip")
{ {
CanBeFocused = false CanBeFocused = false,
Visible = false
}; };
var hullInfoContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), hullInfoFrame.RectTransform, Anchor.Center)) var hullInfoContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), hullInfoFrame.RectTransform, Anchor.Center))
@@ -431,7 +432,7 @@ namespace Barotrauma.Items.Components
scissorComponent = new GUIScissorComponent(new RectTransform(Vector2.One, submarineContainer.RectTransform, Anchor.Center)); scissorComponent = new GUIScissorComponent(new RectTransform(Vector2.One, submarineContainer.RectTransform, Anchor.Center));
miniMapContainer = new GUIFrame(new RectTransform(Vector2.One, scissorComponent.Content.RectTransform, Anchor.Center), style: null) { CanBeFocused = false }; miniMapContainer = new GUIFrame(new RectTransform(Vector2.One, scissorComponent.Content.RectTransform, Anchor.Center), style: null) { CanBeFocused = false };
ImmutableHashSet<Item> hullPointsOfInterest = Item.ItemList.Where(it => it.Submarine == item.Submarine && !it.HiddenInGame && !it.NonInteractable && it.Prefab.ShowInStatusMonitor && it.GetComponent<Door>() != null).ToImmutableHashSet(); ImmutableHashSet<Item> hullPointsOfInterest = Item.ItemList.Where(it => it.Submarine == item.Submarine && !it.HiddenInGame && !it.NonInteractable && it.Prefab.ShowInStatusMonitor && (it.GetComponent<Door>() != null || it.GetComponent<Turret>() != null)).ToImmutableHashSet();
miniMapFrame = CreateMiniMap(item.Submarine, submarineContainer, MiniMapSettings.Default, hullPointsOfInterest, out hullStatusComponents); miniMapFrame = CreateMiniMap(item.Submarine, submarineContainer, MiniMapSettings.Default, hullPointsOfInterest, out hullStatusComponents);
IEnumerable<Item> electrialPointsOfInterest = Item.ItemList.Where(it => it.Submarine == item.Submarine && !it.HiddenInGame && !it.NonInteractable && it.GetComponent<Repairable>() != null); IEnumerable<Item> electrialPointsOfInterest = Item.ItemList.Where(it => it.Submarine == item.Submarine && !it.HiddenInGame && !it.NonInteractable && it.GetComponent<Repairable>() != null);
@@ -460,29 +461,63 @@ namespace Barotrauma.Items.Components
electricalChildren = electricChildren.ToImmutableDictionary(); electricalChildren = electricChildren.ToImmutableDictionary();
Dictionary<MiniMapGUIComponent, GUIComponent> doorChilds = new Dictionary<MiniMapGUIComponent, GUIComponent>(); Dictionary<MiniMapGUIComponent, GUIComponent> doorChilds = new Dictionary<MiniMapGUIComponent, GUIComponent>();
Dictionary<MiniMapGUIComponent, GUIComponent> weaponChilds = new Dictionary<MiniMapGUIComponent, GUIComponent>();
foreach (var (entity, component) in hullStatusComponents) foreach (var (entity, component) in hullStatusComponents)
{ {
if (!hullPointsOfInterest.Contains(entity)) { continue; } if (!hullPointsOfInterest.Contains(entity)) { continue; }
const int minSize = 8; if (!(entity is Item it)) { continue; }
const int borderMaxSize = 2; const int borderMaxSize = 2;
Point size = component.BorderComponent.Rect.Size; if (it.GetComponent<Door>() is { })
size.X = Math.Max(size.X, minSize);
size.Y = Math.Max(size.Y, minSize);
float width = Math.Min(borderMaxSize, Math.Min(size.X, size.Y) / 8f);
GUIFrame frame = new GUIFrame(new RectTransform(size, component.RectComponent.RectTransform, anchor: Anchor.Center), style: "ScanLines", color: DoorIndicatorColor)
{ {
OutlineColor = GUI.Style.Green, const int minSize = 8;
OutlineThickness = width
}; Point size = component.BorderComponent.Rect.Size;
doorChilds.Add(component, frame);
size.X = Math.Max(size.X, minSize);
size.Y = Math.Max(size.Y, minSize);
float width = Math.Min(borderMaxSize, Math.Min(size.X, size.Y) / 8f);
GUIFrame frame = new GUIFrame(new RectTransform(size, component.RectComponent.RectTransform, anchor: Anchor.Center), style: "ScanLines", color: DoorIndicatorColor)
{
OutlineColor = DoorIndicatorColor,
OutlineThickness = width
};
doorChilds.Add(component, frame);
}
else if (it.GetComponent<Turret>() is { } turret)
{
int parentWidth = (int) (submarineContainer.Rect.Width / 16f);
GUICustomComponent frame = new GUICustomComponent(new RectTransform(new Point(parentWidth, parentWidth), component.RectComponent.RectTransform, anchor: Anchor.Center), (batch, customComponent) =>
{
Vector2 center = customComponent.Center;
float rotation = turret.Rotation;
if (!hasPower)
{
float minRotation = MathHelper.ToRadians(Math.Min(turret.RotationLimits.X, turret.RotationLimits.Y)),
maxRotation = MathHelper.ToRadians(Math.Max(turret.RotationLimits.X, turret.RotationLimits.Y));
rotation = (minRotation + maxRotation) / 2;
}
if (turret.WeaponIndicatorSprite is { } weaponSprite)
{
Vector2 origin = weaponSprite.Origin;
float scale = parentWidth / Math.Max(weaponSprite.size.X, weaponSprite.size.Y);
Color color = !hasPower ? NoPowerColor : turret.ActiveUser is null ? GUI.Style.Red : GUI.Style.Green;
weaponSprite.Draw(batch, center, color, origin, rotation, scale, it.SpriteEffects);
}
});
weaponChilds.Add(component, frame);
}
} }
doorChildren = doorChilds.ToImmutableDictionary(); doorChildren = doorChilds.ToImmutableDictionary();
weaponChildren = weaponChilds.ToImmutableDictionary();
Rectangle parentRect = miniMapFrame.Rect; Rectangle parentRect = miniMapFrame.Rect;
@@ -655,7 +690,7 @@ namespace Barotrauma.Items.Components
worldBorders.Location += item.Submarine.WorldPosition.ToPoint(); worldBorders.Location += item.Submarine.WorldPosition.ToPoint();
foreach (Gap gap in Gap.GapList) foreach (Gap gap in Gap.GapList)
{ {
if (gap.IsRoomToRoom || gap.Submarine != item.Submarine || gap.ConnectedDoor != null) { continue; } if (gap.IsRoomToRoom || gap.Submarine != item.Submarine || gap.ConnectedDoor != null || gap.HiddenInGame) { continue; }
RectangleF entityRect = ScaleRectToUI(gap, miniMapFrame.Rect, worldBorders); RectangleF entityRect = ScaleRectToUI(gap, miniMapFrame.Rect, worldBorders);
Vector2 scale = new Vector2(entityRect.Size.X / spriteSize.X, entityRect.Size.Y / spriteSize.Y) * 2.0f; Vector2 scale = new Vector2(entityRect.Size.X / spriteSize.X, entityRect.Size.Y / spriteSize.Y) * 2.0f;
@@ -668,13 +703,33 @@ namespace Barotrauma.Items.Components
} }
} }
if (currentMode == MiniMapMode.HullStatus) if (currentMode == MiniMapMode.HullStatus && hullStatusComponents != null)
{ {
foreach (var (entity, component) in hullStatusComponents) foreach (var (entity, component) in hullStatusComponents)
{ {
if (!(entity is Hull hull)) { continue; } if (!(entity is Hull hull)) { continue; }
if (!hullDatas.TryGetValue(hull, out HullData? hullData) || hullData is null) { continue; } if (!hullDatas.TryGetValue(hull, out HullData? hullData) || hullData is null) { continue; }
DrawHullCards(spriteBatch, hull, hullData, component.RectComponent); DrawHullCards(spriteBatch, hull, hullData, component.RectComponent);
if (item.CurrentHull is { } currentHull && currentHull == hull)
{
Sprite pingCircle = GUI.Style.YouAreHereCircle.Sprite;
if (pingCircle is null) { continue; }
Vector2 charPos = item.WorldPosition;
Vector2 hullPos = hull.WorldRect.Location.ToVector2(),
hullSize = hull.WorldRect.Size.ToVector2();
Vector2 relativePos = (charPos - hullPos) / hullSize * component.RectComponent.Rect.Size.ToVector2();
relativePos.Y = -relativePos.Y;
float parentWidth = submarineContainer.Rect.Width / 64f;
float spriteSize = pingCircle.size.X * (parentWidth / pingCircle.size.X);
Vector2 drawPos = component.RectComponent.Rect.Location.ToVector2() + relativePos;
drawPos -= new Vector2(spriteSize, spriteSize) / 2f;
pingCircle.Draw(spriteBatch, drawPos, GUI.Style.Red * 0.8f, Vector2.Zero, 0f, parentWidth / pingCircle.size.X);
}
} }
} }
@@ -944,7 +999,7 @@ namespace Barotrauma.Items.Components
if (ShowHullIntegrity) if (ShowHullIntegrity)
{ {
float amount = 1f + hullData.LinkedHulls.Count; float amount = 1f + hullData.LinkedHulls.Count;
gapOpenSum = hull.ConnectedGaps.Concat(hullData.LinkedHulls.SelectMany(h => h.ConnectedGaps)).Where(g => !g.IsRoomToRoom).Sum(g => g.Open) / amount; gapOpenSum = hull.ConnectedGaps.Concat(hullData.LinkedHulls.SelectMany(h => h.ConnectedGaps)).Where(g => !g.IsRoomToRoom && !g.HiddenInGame).Sum(g => g.Open) / amount;
borderColor = Color.Lerp(neutralColor, GUI.Style.Red, Math.Min(gapOpenSum, 1.0f)); borderColor = Color.Lerp(neutralColor, GUI.Style.Red, Math.Min(gapOpenSum, 1.0f));
} }
@@ -1039,10 +1094,9 @@ namespace Barotrauma.Items.Components
} }
else if (it.GetComponent<PowerTransfer>() is { } powerTransfer) else if (it.GetComponent<PowerTransfer>() is { } powerTransfer)
{ {
int current = (int) -powerTransfer.CurrPowerConsumption, int current = (int)-powerTransfer.CurrPowerConsumption, load = (int)powerTransfer.PowerLoad;
load = (int) powerTransfer.PowerLoad;
line1 = TextManager.GetWithVariable("statusmonitor.junctioncurrent.tooltip", "[amount]", current.ToString()); line1 = TextManager.GetWithVariable("statusmonitor.junctionpower.tooltip", "[amount]", current.ToString(), fallBackTag: "statusmonitor.junctioncurrent.tooltip");
line2 = TextManager.GetWithVariable("statusmonitor.junctionload.tooltip", "[amount]", load.ToString()); line2 = TextManager.GetWithVariable("statusmonitor.junctionload.tooltip", "[amount]", load.ToString());
} }
@@ -1086,38 +1140,41 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
bool hullsVisible = currentMode == MiniMapMode.HullStatus; bool hullsVisible = currentMode == MiniMapMode.HullStatus && item.Submarine != null;
foreach (var (entity, component) in hullStatusComponents) if (hullStatusComponents != null)
{ {
if (!(entity is Hull hull)) { continue; } foreach (var (entity, component) in hullStatusComponents)
if (!hullDatas.TryGetValue(hull, out HullData? hullData) || hullData is null) { continue; }
if (hullData.Distort) { continue; }
GUIComponent hullFrame = component.RectComponent;
if (hullsVisible && hullData.HullWaterAmount is { } waterAmount)
{ {
if (hullFrame.Rect.Height * waterAmount > 3.0f) if (!(entity is Hull hull)) { continue; }
if (!hullDatas.TryGetValue(hull, out HullData? hullData) || hullData is null) { continue; }
if (hullData.Distort) { continue; }
GUIComponent hullFrame = component.RectComponent;
if (hullsVisible && hullData.HullWaterAmount is { } waterAmount)
{ {
RectangleF waterRect = new RectangleF(hullFrame.Rect.X, hullFrame.Rect.Y + hullFrame.Rect.Height * (1.0f - waterAmount), hullFrame.Rect.Width, hullFrame.Rect.Height * waterAmount); if (hullFrame.Rect.Height * waterAmount > 3.0f)
const float width = 1f;
GUI.DrawFilledRectangle(spriteBatch, waterRect, HullWaterColor);
if (!MathUtils.NearlyEqual(waterAmount, 1.0f))
{ {
Vector2 offset = new Vector2(0, width); RectangleF waterRect = new RectangleF(hullFrame.Rect.X, hullFrame.Rect.Y + hullFrame.Rect.Height * (1.0f - waterAmount), hullFrame.Rect.Width, hullFrame.Rect.Height * waterAmount);
GUI.DrawLine(spriteBatch, waterRect.Location + offset, new Vector2(waterRect.Right, waterRect.Y) + offset, HullWaterLineColor, width: width);
const float width = 1f;
GUI.DrawFilledRectangle(spriteBatch, waterRect, HullWaterColor);
if (!MathUtils.NearlyEqual(waterAmount, 1.0f))
{
Vector2 offset = new Vector2(0, width);
GUI.DrawLine(spriteBatch, waterRect.Location + offset, new Vector2(waterRect.Right, waterRect.Y) + offset, HullWaterLineColor, width: width);
}
} }
} }
}
if (hullsVisible && hullData.HullOxygenAmount is { } oxygenAmount) if (hullsVisible && hullData.HullOxygenAmount is { } oxygenAmount)
{ {
GUI.DrawRectangle(spriteBatch, hullFrame.Rect, Color.Lerp(GUI.Style.Red * 0.5f, GUI.Style.Green * 0.3f, oxygenAmount / 100.0f), true); GUI.DrawRectangle(spriteBatch, hullFrame.Rect, Color.Lerp(GUI.Style.Red * 0.5f, GUI.Style.Green * 0.3f, oxygenAmount / 100.0f), true);
}
} }
} }
} }
@@ -1221,7 +1278,7 @@ namespace Barotrauma.Items.Components
Vector2 spriteScale = new Vector2(entityRect.Size.X / sprite.size.X, entityRect.Size.Y / sprite.size.Y); Vector2 spriteScale = new Vector2(entityRect.Size.X / sprite.size.X, entityRect.Size.Y / sprite.size.Y);
Vector2 origin = new Vector2(sprite.Origin.X * spriteScale.X, sprite.Origin.Y * spriteScale.Y); Vector2 origin = new Vector2(sprite.Origin.X * spriteScale.X, sprite.Origin.Y * spriteScale.Y);
if (item.GetComponent<Turret>() is { } turret) if (!item.Prefab.ShowInStatusMonitor && item.GetComponent<Turret>() is { } turret)
{ {
Vector2 drawPos = turret.GetDrawPos(); Vector2 drawPos = turret.GetDrawPos();
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
@@ -158,7 +158,7 @@ namespace Barotrauma.Items.Components
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), columnLeft.RectTransform), style: "HorizontalLine"); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), columnLeft.RectTransform), style: "HorizontalLine");
float relativeYMargin = 0.02f; float relativeYMargin = 0.02f;
Vector2 relativeTextSize = new Vector2(0.9f, 0.2f); Vector2 relativeTextSize = new Vector2(0.9f, 0.15f);
Vector2 sliderSize = new Vector2(1.0f, 0.125f); Vector2 sliderSize = new Vector2(1.0f, 0.125f);
Vector2 meterSize = new Vector2(1, 1 - relativeTextSize.Y - relativeYMargin - sliderSize.Y - 0.1f); Vector2 meterSize = new Vector2(1, 1 - relativeTextSize.Y - relativeYMargin - sliderSize.Y - 0.1f);
@@ -198,7 +198,7 @@ namespace Barotrauma.Items.Components
FissionRateScrollBar = new GUIScrollBar(new RectTransform(sliderSize, leftArea.RectTransform, Anchor.TopCenter) FissionRateScrollBar = new GUIScrollBar(new RectTransform(sliderSize, leftArea.RectTransform, Anchor.TopCenter)
{ {
RelativeOffset = new Vector2(0, fissionMeter.RectTransform.RelativeOffset.Y + meterSize.Y) RelativeOffset = new Vector2(0, fissionMeter.RectTransform.RelativeOffset.Y + meterSize.Y + relativeYMargin)
}, },
style: "DeviceSlider", barSize: 0.15f) style: "DeviceSlider", barSize: 0.15f)
{ {
@@ -208,7 +208,7 @@ namespace Barotrauma.Items.Components
{ {
LastUser = Character.Controlled; LastUser = Character.Controlled;
unsentChanges = true; unsentChanges = true;
targetFissionRate = scrollAmount * 100.0f; TargetFissionRate = scrollAmount * 100.0f;
return false; return false;
} }
@@ -216,7 +216,7 @@ namespace Barotrauma.Items.Components
TurbineOutputScrollBar = new GUIScrollBar(new RectTransform(sliderSize, rightArea.RectTransform, Anchor.TopCenter) TurbineOutputScrollBar = new GUIScrollBar(new RectTransform(sliderSize, rightArea.RectTransform, Anchor.TopCenter)
{ {
RelativeOffset = new Vector2(0, turbineMeter.RectTransform.RelativeOffset.Y + meterSize.Y) RelativeOffset = new Vector2(0, turbineMeter.RectTransform.RelativeOffset.Y + meterSize.Y + relativeYMargin)
}, },
style: "DeviceSlider", barSize: 0.15f, isHorizontal: true) style: "DeviceSlider", barSize: 0.15f, isHorizontal: true)
{ {
@@ -226,7 +226,7 @@ namespace Barotrauma.Items.Components
{ {
LastUser = Character.Controlled; LastUser = Character.Controlled;
unsentChanges = true; unsentChanges = true;
targetTurbineOutput = scrollAmount * 100.0f; TargetTurbineOutput = scrollAmount * 100.0f;
return false; return false;
} }
@@ -370,7 +370,7 @@ namespace Barotrauma.Items.Components
}; };
string loadStr = TextManager.Get("ReactorLoad"); string loadStr = TextManager.Get("ReactorLoad");
string kW = TextManager.Get("kilowatt"); string kW = TextManager.Get("kilowatt");
loadText.TextGetter += () => $"{loadStr.Replace("[kw]", ((int)load).ToString())} {kW}"; loadText.TextGetter += () => $"{loadStr.Replace("[kw]", ((int)Load).ToString())} {kW}";
var graph = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), graphArea.RectTransform), style: "InnerFrameRed"); var graph = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), graphArea.RectTransform), style: "InnerFrameRed");
new GUICustomComponent(new RectTransform(new Vector2(0.9f, 0.98f), graph.RectTransform, Anchor.Center), DrawGraph, null); new GUICustomComponent(new RectTransform(new Vector2(0.9f, 0.98f), graph.RectTransform, Anchor.Center), DrawGraph, null);
@@ -387,8 +387,8 @@ namespace Barotrauma.Items.Components
public override void OnItemLoaded() public override void OnItemLoaded()
{ {
base.OnItemLoaded(); base.OnItemLoaded();
TurbineOutputScrollBar.BarScroll = targetTurbineOutput / 100.0f; TurbineOutputScrollBar.BarScroll = TargetTurbineOutput / 100.0f;
FissionRateScrollBar.BarScroll = targetFissionRate / 100.0f; FissionRateScrollBar.BarScroll = TargetFissionRate / 100.0f;
var itemContainer = item.GetComponent<ItemContainer>(); var itemContainer = item.GetComponent<ItemContainer>();
if (itemContainer != null) if (itemContainer != null)
{ {
@@ -462,7 +462,7 @@ namespace Barotrauma.Items.Components
if (graphTimer > updateGraphInterval) if (graphTimer > updateGraphInterval)
{ {
UpdateGraph(outputGraph, -currPowerConsumption); UpdateGraph(outputGraph, -currPowerConsumption);
UpdateGraph(loadGraph, load); UpdateGraph(loadGraph, Load);
graphTimer = 0.0f; graphTimer = 0.0f;
} }
@@ -487,7 +487,7 @@ namespace Barotrauma.Items.Components
float jitter = 0.0f; float jitter = 0.0f;
if (FissionRate > allowedFissionRate.Y - 5.0f) if (FissionRate > allowedFissionRate.Y - 5.0f)
{ {
float jitterAmount = Math.Min(targetFissionRate - allowedFissionRate.Y, 10.0f); float jitterAmount = Math.Min(TargetFissionRate - allowedFissionRate.Y, 10.0f);
float t = graphTimer / updateGraphInterval; float t = graphTimer / updateGraphInterval;
jitter = (PerlinNoise.GetPerlin(t * 0.5f, t * 0.1f) - 0.5f) * jitterAmount; jitter = (PerlinNoise.GetPerlin(t * 0.5f, t * 0.1f) - 0.5f) * jitterAmount;
@@ -525,12 +525,12 @@ namespace Barotrauma.Items.Components
criticalHeatWarning.Selected = temperature > allowedTemperature.Y && lightOn; criticalHeatWarning.Selected = temperature > allowedTemperature.Y && lightOn;
lowTemperatureWarning.Selected = temperature < allowedTemperature.X && lightOn; lowTemperatureWarning.Selected = temperature < allowedTemperature.X && lightOn;
criticalOutputWarning.Selected = -currPowerConsumption > load * 1.5f && lightOn; criticalOutputWarning.Selected = -currPowerConsumption > Load * 1.5f && lightOn;
warningButtons["ReactorWarningOverheating"].Selected = temperature > optimalTemperature.Y && lightOn; warningButtons["ReactorWarningOverheating"].Selected = temperature > optimalTemperature.Y && lightOn;
warningButtons["ReactorWarningHighOutput"].Selected = -currPowerConsumption > load * 1.1f && lightOn; warningButtons["ReactorWarningHighOutput"].Selected = -currPowerConsumption > Load * 1.1f && lightOn;
warningButtons["ReactorWarningLowTemp"].Selected = temperature < optimalTemperature.X && lightOn; warningButtons["ReactorWarningLowTemp"].Selected = temperature < optimalTemperature.X && lightOn;
warningButtons["ReactorWarningLowOutput"].Selected = -currPowerConsumption < load * 0.9f && lightOn; warningButtons["ReactorWarningLowOutput"].Selected = -currPowerConsumption < Load * 0.9f && lightOn;
warningButtons["ReactorWarningFuelOut"].Selected = prevAvailableFuel < fissionRate * 0.01f && lightOn; warningButtons["ReactorWarningFuelOut"].Selected = prevAvailableFuel < fissionRate * 0.01f && lightOn;
warningButtons["ReactorWarningLowFuel"].Selected = prevAvailableFuel < fissionRate && lightOn; warningButtons["ReactorWarningLowFuel"].Selected = prevAvailableFuel < fissionRate && lightOn;
warningButtons["ReactorWarningMeltdown"].Selected = meltDownTimer > MeltdownDelay * 0.5f || item.Condition == 0.0f && lightOn; warningButtons["ReactorWarningMeltdown"].Selected = meltDownTimer > MeltdownDelay * 0.5f || item.Condition == 0.0f && lightOn;
@@ -571,12 +571,12 @@ namespace Barotrauma.Items.Components
unsentChanges = true; unsentChanges = true;
if (input.X != 0.0f && GUIScrollBar.DraggingBar != FissionRateScrollBar) if (input.X != 0.0f && GUIScrollBar.DraggingBar != FissionRateScrollBar)
{ {
targetFissionRate = MathHelper.Clamp(targetFissionRate + input.X, 0.0f, 100.0f); TargetFissionRate = MathHelper.Clamp(TargetFissionRate + input.X, 0.0f, 100.0f);
FissionRateScrollBar.BarScroll += input.X / 100.0f; FissionRateScrollBar.BarScroll += input.X / 100.0f;
} }
if (input.Y != 0.0f && GUIScrollBar.DraggingBar != TurbineOutputScrollBar) if (input.Y != 0.0f && GUIScrollBar.DraggingBar != TurbineOutputScrollBar)
{ {
targetTurbineOutput = MathHelper.Clamp(targetTurbineOutput + input.Y, 0.0f, 100.0f); TargetTurbineOutput = MathHelper.Clamp(TargetTurbineOutput + input.Y, 0.0f, 100.0f);
TurbineOutputScrollBar.BarScroll += input.Y / 100.0f; TurbineOutputScrollBar.BarScroll += input.Y / 100.0f;
} }
} }
@@ -596,7 +596,7 @@ namespace Barotrauma.Items.Components
MathHelper.Clamp((allowedRange.X - range.X) / (range.Y - range.X), 0.0f, 0.95f), MathHelper.Clamp((allowedRange.X - range.X) / (range.Y - range.X), 0.0f, 0.95f),
MathHelper.Clamp((allowedRange.Y - range.X) / (range.Y - range.X), 0.0f, 1.0f)); MathHelper.Clamp((allowedRange.Y - range.X) / (range.Y - range.X), 0.0f, 1.0f));
Vector2 sectorRad = new Vector2(-1.57f, 1.57f); Vector2 sectorRad = new Vector2(-1.35f, 1.35f);
Vector2 optimalSectorRad = new Vector2( Vector2 optimalSectorRad = new Vector2(
MathHelper.Lerp(sectorRad.X, sectorRad.Y, optimalRangeNormalized.X), MathHelper.Lerp(sectorRad.X, sectorRad.Y, optimalRangeNormalized.X),
@@ -606,23 +606,25 @@ namespace Barotrauma.Items.Components
MathHelper.Lerp(sectorRad.X, sectorRad.Y, allowedRangeNormalized.X), MathHelper.Lerp(sectorRad.X, sectorRad.Y, allowedRangeNormalized.X),
MathHelper.Lerp(sectorRad.X, sectorRad.Y, allowedRangeNormalized.Y)); MathHelper.Lerp(sectorRad.X, sectorRad.Y, allowedRangeNormalized.Y));
Vector2 pointerPos = pos - new Vector2(0, 30) * scale;
if (optimalRangeNormalized.X == optimalRangeNormalized.Y) if (optimalRangeNormalized.X == optimalRangeNormalized.Y)
{ {
sectorSprite.Draw(spriteBatch, pos, GUI.Style.Red, MathHelper.PiOver2, scale); sectorSprite.Draw(spriteBatch, pointerPos, GUI.Style.Red, MathHelper.PiOver2, scale);
} }
else else
{ {
spriteBatch.End(); spriteBatch.End();
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle; Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(0, 0, GameMain.GraphicsWidth, (int)(pos.Y + (meterSprite.size.Y - meterSprite.Origin.Y) * scale) - 3); spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(0, 0, GameMain.GraphicsWidth, (int)(pointerPos.Y + (meterSprite.size.Y - meterSprite.Origin.Y) * scale) - 3);
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable); spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
float scaleMultiplier = 0.95f; float scaleMultiplier = 0.95f;
sectorSprite.Draw(spriteBatch, pos, optimalRangeColor, MathHelper.PiOver2 + (allowedSectorRad.X + allowedSectorRad.Y) / 2.0f, scale * scaleMultiplier); sectorSprite.Draw(spriteBatch, pointerPos, optimalRangeColor, MathHelper.PiOver2 + (allowedSectorRad.X + allowedSectorRad.Y) / 2.0f, scale * scaleMultiplier);
sectorSprite.Draw(spriteBatch, pos, offRangeColor, optimalSectorRad.X, scale * scaleMultiplier); sectorSprite.Draw(spriteBatch, pointerPos, offRangeColor, optimalSectorRad.X, scale * scaleMultiplier);
sectorSprite.Draw(spriteBatch, pos, warningColor, allowedSectorRad.X, scale * scaleMultiplier); sectorSprite.Draw(spriteBatch, pointerPos, warningColor, allowedSectorRad.X, scale * scaleMultiplier);
sectorSprite.Draw(spriteBatch, pos, offRangeColor, MathHelper.Pi + optimalSectorRad.Y, scale * scaleMultiplier); sectorSprite.Draw(spriteBatch, pointerPos, offRangeColor, MathHelper.Pi + optimalSectorRad.Y, scale * scaleMultiplier);
sectorSprite.Draw(spriteBatch, pos, warningColor, MathHelper.Pi + allowedSectorRad.Y, scale * scaleMultiplier); sectorSprite.Draw(spriteBatch, pointerPos, warningColor, MathHelper.Pi + allowedSectorRad.Y, scale * scaleMultiplier);
spriteBatch.End(); spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect; spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
@@ -634,7 +636,7 @@ namespace Barotrauma.Items.Components
float normalizedValue = (value - range.X) / (range.Y - range.X); float normalizedValue = (value - range.X) / (range.Y - range.X);
float valueRad = MathHelper.Lerp(sectorRad.X, sectorRad.Y, normalizedValue); float valueRad = MathHelper.Lerp(sectorRad.X, sectorRad.Y, normalizedValue);
Vector2 offset = new Vector2(0, 40) * scale; Vector2 offset = new Vector2(0, 40) * scale;
meterPointer.Draw(spriteBatch, pos - offset, valueRad, scale); meterPointer.Draw(spriteBatch, pointerPos, valueRad, scale);
} }
static void UpdateGraph<T>(IList<T> graph, T newValue) static void UpdateGraph<T>(IList<T> graph, T newValue)
@@ -713,8 +715,8 @@ namespace Barotrauma.Items.Components
{ {
msg.Write(autoTemp); msg.Write(autoTemp);
msg.Write(PowerOn); msg.Write(PowerOn);
msg.WriteRangedSingle(targetFissionRate, 0.0f, 100.0f, 8); msg.WriteRangedSingle(TargetFissionRate, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(targetTurbineOutput, 0.0f, 100.0f, 8); msg.WriteRangedSingle(TargetTurbineOutput, 0.0f, 100.0f, 8);
correctionTimer = CorrectionDelay; correctionTimer = CorrectionDelay;
} }
@@ -730,17 +732,17 @@ namespace Barotrauma.Items.Components
AutoTemp = msg.ReadBoolean(); AutoTemp = msg.ReadBoolean();
PowerOn = msg.ReadBoolean(); PowerOn = msg.ReadBoolean();
Temperature = msg.ReadRangedSingle(0.0f, 100.0f, 8); Temperature = msg.ReadRangedSingle(0.0f, 100.0f, 8);
targetFissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8); TargetFissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
targetTurbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8); TargetTurbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8);
degreeOfSuccess = msg.ReadRangedSingle(0.0f, 1.0f, 8); degreeOfSuccess = msg.ReadRangedSingle(0.0f, 1.0f, 8);
if (Math.Abs(FissionRateScrollBar.BarScroll - targetFissionRate / 100.0f) > 0.01f) if (Math.Abs(FissionRateScrollBar.BarScroll - TargetFissionRate / 100.0f) > 0.01f)
{ {
FissionRateScrollBar.BarScroll = targetFissionRate / 100.0f; FissionRateScrollBar.BarScroll = TargetFissionRate / 100.0f;
} }
if (Math.Abs(TurbineOutputScrollBar.BarScroll - targetTurbineOutput / 100.0f) > 0.01f) if (Math.Abs(TurbineOutputScrollBar.BarScroll - TargetTurbineOutput / 100.0f) > 0.01f)
{ {
TurbineOutputScrollBar.BarScroll = targetTurbineOutput / 100.0f; TurbineOutputScrollBar.BarScroll = TargetTurbineOutput / 100.0f;
} }
IsActive = true; IsActive = true;
@@ -456,7 +456,7 @@ namespace Barotrauma.Items.Components
} }
} }
float distort = 1.0f - item.Condition / item.MaxCondition; float distort = MathHelper.Clamp(1.0f - item.Condition / item.MaxCondition, 0.0f, 1.0f);
for (int i = sonarBlips.Count - 1; i >= 0; i--) for (int i = sonarBlips.Count - 1; i >= 0; i--)
{ {
sonarBlips[i].FadeTimer -= deltaTime * MathHelper.Lerp(0.5f, 2.0f, distort); sonarBlips[i].FadeTimer -= deltaTime * MathHelper.Lerp(0.5f, 2.0f, distort);
@@ -56,7 +56,9 @@ namespace Barotrauma.Items.Components
if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; } if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; }
float maxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(character); float maxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(character);
if (item.Condition / maxRepairConditionMultiplier < RepairThreshold) { return true; } float defaultMaxCondition = item.MaxCondition / maxRepairConditionMultiplier;
if (MathUtils.Percentage(item.Condition, defaultMaxCondition) < RepairThreshold) { return true; }
if (CurrentFixer == character) if (CurrentFixer == character)
{ {
@@ -280,14 +282,14 @@ namespace Barotrauma.Items.Components
progressBarOverlayText.Visible = false; progressBarOverlayText.Visible = false;
} }
RepairButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Repair)) && !item.IsFullCondition && item.ConditionPercentage < RepairThreshold; RepairButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Repair)) && !item.IsFullCondition && IsBelowRepairThreshold;
RepairButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Repair) ? RepairButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Repair) ?
repairButtonText : repairButtonText :
repairingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1); repairingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
SabotageButton.Visible = character.IsTraitor; SabotageButton.Visible = character.IsTraitor;
SabotageButton.IgnoreLayoutGroups = !SabotageButton.Visible; SabotageButton.IgnoreLayoutGroups = !SabotageButton.Visible;
SabotageButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Sabotage)) && character.IsTraitor && item.ConditionPercentage > MinSabotageCondition; SabotageButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Sabotage)) && character.IsTraitor && IsBelowRepairThreshold;
SabotageButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Sabotage || !character.IsTraitor) ? SabotageButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Sabotage || !character.IsTraitor) ?
sabotageButtonText : sabotageButtonText :
sabotagingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1); sabotagingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
@@ -35,7 +35,7 @@ namespace Barotrauma.Items.Components
new GUIFrame(new RectTransform(new Vector2(0.9f, 0.01f), layoutGroup.RectTransform), style: "HorizontalLine"); new GUIFrame(new RectTransform(new Vector2(0.9f, 0.01f), layoutGroup.RectTransform), style: "HorizontalLine");
inputBox = new GUITextBox(new RectTransform(new Vector2(1, .1f), layoutGroup.RectTransform), textColor: Color.LimeGreen) inputBox = new GUITextBox(new RectTransform(new Vector2(1, .1f), layoutGroup.RectTransform), textColor: TextColor)
{ {
MaxTextLength = MaxMessageLength, MaxTextLength = MaxMessageLength,
OverflowClip = true, OverflowClip = true,
@@ -63,15 +63,15 @@ namespace Barotrauma.Items.Components
} }
OutputValue = input; OutputValue = input;
ShowOnDisplay(input, addToHistory: true); ShowOnDisplay(input, addToHistory: true, TextColor);
item.SendSignal(input, "signal_out"); item.SendSignal(input, "signal_out");
} }
partial void ShowOnDisplay(string input, bool addToHistory) partial void ShowOnDisplay(string input, bool addToHistory, Color color)
{ {
if (addToHistory) if (addToHistory)
{ {
messageHistory.Add(input); messageHistory.Add(new TerminalMessage(input, color));
while (messageHistory.Count > MaxMessages) while (messageHistory.Count > MaxMessages)
{ {
messageHistory.RemoveAt(0); messageHistory.RemoveAt(0);
@@ -85,7 +85,7 @@ namespace Barotrauma.Items.Components
GUITextBlock newBlock = new GUITextBlock( GUITextBlock newBlock = new GUITextBlock(
new RectTransform(new Vector2(1, 0), historyBox.Content.RectTransform, anchor: Anchor.TopCenter), new RectTransform(new Vector2(1, 0), historyBox.Content.RectTransform, anchor: Anchor.TopCenter),
"> " + input, "> " + input,
textColor: Color.LimeGreen, wrap: true, font: UseMonospaceFont ? GUI.MonospacedFont : GUI.GlobalFont) textColor: color, wrap: true, font: UseMonospaceFont ? GUI.MonospacedFont : GUI.GlobalFont)
{ {
CanBeFocused = false CanBeFocused = false
}; };
@@ -0,0 +1,12 @@
using Barotrauma.Networking;
namespace Barotrauma.Items.Components
{
partial class TriggerComponent : ItemComponent, IServerSerializable
{
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
CurrentForceFluctuation = msg.ReadRangedSingle(0.0f, 1.0f, 8);
}
}
}
@@ -14,6 +14,7 @@ namespace Barotrauma.Items.Components
partial class Turret : Powered, IDrawableComponent, IServerSerializable partial class Turret : Powered, IDrawableComponent, IServerSerializable
{ {
private Sprite crosshairSprite, crosshairPointerSprite; private Sprite crosshairSprite, crosshairPointerSprite;
public Sprite WeaponIndicatorSprite;
private GUIProgressBar powerIndicator; private GUIProgressBar powerIndicator;
@@ -134,6 +135,9 @@ namespace Barotrauma.Items.Components
case "crosshair": case "crosshair":
crosshairSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.FilePath)); crosshairSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.FilePath));
break; break;
case "weaponindicator":
WeaponIndicatorSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.FilePath));
break;
case "crosshairpointer": case "crosshairpointer":
crosshairPointerSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.FilePath)); crosshairPointerSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.FilePath));
break; break;
@@ -101,7 +101,7 @@ namespace Barotrauma
private float currentHighlightState, fadeInDuration, fadeOutDuration; private float currentHighlightState, fadeInDuration, fadeOutDuration;
private Color currentHighlightColor; private Color currentHighlightColor;
private IEnumerable<object> UpdateBorderHighlight() private IEnumerable<CoroutineStatus> UpdateBorderHighlight()
{ {
HighlightTimer = 1.0f; HighlightTimer = 1.0f;
while (currentHighlightState < fadeInDuration + fadeOutDuration) while (currentHighlightState < fadeInDuration + fadeOutDuration)
@@ -1255,9 +1255,18 @@ namespace Barotrauma
else else
{ {
bool anySuccess = false; bool anySuccess = false;
bool allowCombine = true;
//if we're dragging a stack of partial items or trying to drag to a stack of partial items
//(which should not normally exist, but can happen when e.g. fire damages a stack of items)
//don't allow combining because it leads to weird behavior (stack of items of mixed quality)
if (DraggingItems.Count(it => !it.IsFullCondition && it.Condition > 0.0f) > 1 ||
selectedInventory.GetItemsAt(slotIndex).Count(it => !it.IsFullCondition && it.Condition > 0.0f) > 1)
{
allowCombine = false;
}
foreach (Item item in DraggingItems) foreach (Item item in DraggingItems)
{ {
bool success = selectedInventory.TryPutItem(item, slotIndex, allowSwapping: !anySuccess, true, Character.Controlled); bool success = selectedInventory.TryPutItem(item, slotIndex, allowSwapping: !anySuccess, allowCombine, Character.Controlled);
anySuccess |= success; anySuccess |= success;
if (!success) { break; } if (!success) { break; }
} }
@@ -1673,7 +1682,7 @@ namespace Barotrauma
} }
sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale); sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale);
if ((!item.AllowStealing || (inventory != null && inventory.slots[slotIndex].Items.Any(it => !it.AllowStealing))) && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand)) if (((item.SpawnedInCurrentOutpost && !item.AllowStealing) || (inventory != null && inventory.slots[slotIndex].Items.Any(it => it.SpawnedInCurrentOutpost && !it.AllowStealing))) && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
{ {
var stealIcon = CharacterInventory.LimbSlotIcons[InvSlotType.LeftHand]; var stealIcon = CharacterInventory.LimbSlotIcons[InvSlotType.LeftHand];
Vector2 iconSize = new Vector2(25 * GUI.Scale); Vector2 iconSize = new Vector2(25 * GUI.Scale);
@@ -1818,7 +1827,7 @@ namespace Barotrauma
} }
} }
private IEnumerable<object> SyncItemsAfterDelay(UInt16 lastEventID) private IEnumerable<CoroutineStatus> SyncItemsAfterDelay(UInt16 lastEventID)
{ {
while (syncItemsDelay > 0.0f || while (syncItemsDelay > 0.0f ||
//don't apply inventory updates until //don't apply inventory updates until
@@ -175,12 +175,12 @@ namespace Barotrauma
} }
} }
float displayCondition = FakeBroken ? 0.0f : condition; float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
for (int i = 0; i < Prefab.BrokenSprites.Count;i++) for (int i = 0; i < Prefab.BrokenSprites.Count;i++)
{ {
if (Prefab.BrokenSprites[i].FadeIn) { continue; } if (Prefab.BrokenSprites[i].FadeIn) { continue; }
float minCondition = i > 0 ? Prefab.BrokenSprites[i - i].MaxCondition : 0.0f; float minCondition = i > 0 ? Prefab.BrokenSprites[i - i].MaxConditionPercentage : 0.0f;
if (displayCondition <= minCondition || displayCondition <= Prefab.BrokenSprites[i].MaxCondition) if (displayCondition <= minCondition || displayCondition <= Prefab.BrokenSprites[i].MaxConditionPercentage)
{ {
activeSprite = Prefab.BrokenSprites[i].Sprite; activeSprite = Prefab.BrokenSprites[i].Sprite;
break; break;
@@ -284,8 +284,8 @@ namespace Barotrauma
{ {
if (Prefab.BrokenSprites[i].FadeIn) if (Prefab.BrokenSprites[i].FadeIn)
{ {
float min = i > 0 ? Prefab.BrokenSprites[i - i].MaxCondition : 0.0f; float min = i > 0 ? Prefab.BrokenSprites[i - i].MaxConditionPercentage : 0.0f;
float max = Prefab.BrokenSprites[i].MaxCondition; float max = Prefab.BrokenSprites[i].MaxConditionPercentage;
fadeInBrokenSpriteAlpha = 1.0f - ((displayCondition - min) / (max - min)); fadeInBrokenSpriteAlpha = 1.0f - ((displayCondition - min) / (max - min));
if (fadeInBrokenSpriteAlpha > 0.0f && fadeInBrokenSpriteAlpha <= 1.0f) if (fadeInBrokenSpriteAlpha > 0.0f && fadeInBrokenSpriteAlpha <= 1.0f)
{ {
@@ -293,7 +293,7 @@ namespace Barotrauma
} }
continue; continue;
} }
if (displayCondition <= Prefab.BrokenSprites[i].MaxCondition) if (displayCondition <= Prefab.BrokenSprites[i].MaxConditionPercentage)
{ {
activeSprite = Prefab.BrokenSprites[i].Sprite; activeSprite = Prefab.BrokenSprites[i].Sprite;
drawOffset = Prefab.BrokenSprites[i].Offset.ToVector2() * Scale; drawOffset = Prefab.BrokenSprites[i].Offset.ToVector2() * Scale;
@@ -1632,7 +1632,7 @@ namespace Barotrauma
{ {
item = new Item(itemPrefab, pos, sub, id: itemId) item = new Item(itemPrefab, pos, sub, id: itemId)
{ {
SpawnedInOutpost = spawnedInOutpost, SpawnedInCurrentOutpost = spawnedInOutpost,
AllowStealing = allowStealing, AllowStealing = allowStealing,
Quality = quality Quality = quality
}; };
@@ -11,7 +11,7 @@ namespace Barotrauma
class BrokenItemSprite class BrokenItemSprite
{ {
//sprite will be rendered if the condition of the item is below this //sprite will be rendered if the condition of the item is below this
public readonly float MaxCondition; public readonly float MaxConditionPercentage;
public readonly Sprite Sprite; public readonly Sprite Sprite;
public readonly bool FadeIn; public readonly bool FadeIn;
public readonly Point Offset; public readonly Point Offset;
@@ -19,7 +19,7 @@ namespace Barotrauma
public BrokenItemSprite(Sprite sprite, float maxCondition, bool fadeIn, Point offset) public BrokenItemSprite(Sprite sprite, float maxCondition, bool fadeIn, Point offset)
{ {
Sprite = sprite; Sprite = sprite;
MaxCondition = MathHelper.Clamp(maxCondition, 0.0f, 100.0f); MaxConditionPercentage = MathHelper.Clamp(maxCondition, 0.0f, 100.0f);
FadeIn = fadeIn; FadeIn = fadeIn;
Offset = offset; Offset = offset;
} }
@@ -95,7 +95,7 @@ namespace Barotrauma
MathHelper.Clamp(particlePos.Y, hull.WorldRect.Y - hull.WorldRect.Height, hull.WorldRect.Y)); MathHelper.Clamp(particlePos.Y, hull.WorldRect.Y - hull.WorldRect.Height, hull.WorldRect.Y));
} }
private IEnumerable<object> DimLight(LightSource light) private IEnumerable<CoroutineStatus> DimLight(LightSource light)
{ {
float currBrightness = 1.0f; float currBrightness = 1.0f;
while (light.Color.A > 0.0f && flashDuration > 0.0f) while (light.Color.A > 0.0f && flashDuration > 0.0f)
@@ -438,7 +438,7 @@ namespace Barotrauma
for (int i = 0; i < Bodies.Count; i++) for (int i = 0; i < Bodies.Count; i++)
{ {
Vector2 pos = FarseerPhysics.ConvertUnits.ToDisplayUnits(Bodies[i].Position); Vector2 pos = FarseerPhysics.ConvertUnits.ToDisplayUnits(Bodies[i].Position);
if (Submarine != null) pos += Submarine.Position; if (Submarine != null) { pos += Submarine.DrawPosition; }
pos.Y = -pos.Y; pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
pos, pos,
@@ -202,6 +202,7 @@ namespace Barotrauma
} }
} }
GUITextBlock.AutoScaleAndNormalize(parent.Content.GetAllChildren<GUITextBlock>().Where(c => c != submarineNameText && c != descBlock)); GUITextBlock.AutoScaleAndNormalize(parent.Content.GetAllChildren<GUITextBlock>().Where(c => c != submarineNameText && c != descBlock));
parent.ForceLayoutRecalculation();
} }
} }
} }
@@ -160,7 +160,7 @@ namespace Barotrauma.Networking
public TransferInDelegate OnTransferFailed; public TransferInDelegate OnTransferFailed;
private readonly List<FileTransferIn> activeTransfers; private readonly List<FileTransferIn> activeTransfers;
private readonly List<Pair<int, double>> finishedTransfers; private readonly List<(int transferId, double finishedTime)> finishedTransfers;
private readonly Dictionary<FileTransferType, string> downloadFolders = new Dictionary<FileTransferType, string>() private readonly Dictionary<FileTransferType, string> downloadFolders = new Dictionary<FileTransferType, string>()
{ {
@@ -176,7 +176,7 @@ namespace Barotrauma.Networking
public FileReceiver() public FileReceiver()
{ {
activeTransfers = new List<FileTransferIn>(); activeTransfers = new List<FileTransferIn>();
finishedTransfers = new List<Pair<int, double>>(); finishedTransfers = new List<(int transferId, double finishedTime)>();
} }
public void ReadMessage(IReadMessage inc) public void ReadMessage(IReadMessage inc)
@@ -193,8 +193,8 @@ namespace Barotrauma.Networking
case (byte)FileTransferMessageType.Initiate: case (byte)FileTransferMessageType.Initiate:
{ {
byte transferId = inc.ReadByte(); byte transferId = inc.ReadByte();
var existingTransfer = activeTransfers.Find(t => t.ID == transferId); var existingTransfer = activeTransfers.Find(t => t.Connection.EndpointMatches(t.Connection.EndPointString) && t.ID == transferId);
finishedTransfers.RemoveAll(t => t.First == transferId); finishedTransfers.RemoveAll(t => t.transferId == transferId);
byte fileType = inc.ReadByte(); byte fileType = inc.ReadByte();
//ushort chunkLen = inc.ReadUInt16(); //ushort chunkLen = inc.ReadUInt16();
int fileSize = inc.ReadInt32(); int fileSize = inc.ReadInt32();
@@ -211,7 +211,7 @@ namespace Barotrauma.Networking
} }
else //resend acknowledgement packet else //resend acknowledgement packet
{ {
GameMain.Client.UpdateFileTransfer(transferId, 0); GameMain.Client.UpdateFileTransfer(transferId, existingTransfer.Received);
} }
return; return;
} }
@@ -316,14 +316,14 @@ namespace Barotrauma.Networking
{ {
byte transferId = inc.ReadByte(); byte transferId = inc.ReadByte();
var activeTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId); var activeTransfer = activeTransfers.Find(t => t.Connection.EndpointMatches(t.Connection.EndPointString) && t.ID == transferId);
if (activeTransfer == null) if (activeTransfer == null)
{ {
//it's possible for the server to send some extra data //it's possible for the server to send some extra data
//before it acknowledges that the download is finished, //before it acknowledges that the download is finished,
//so let's suppress the error message in that case //so let's suppress the error message in that case
finishedTransfers.RemoveAll(t => t.Second + 5.0 < Timing.TotalTime); finishedTransfers.RemoveAll(t => t.finishedTime + 5.0 < Timing.TotalTime);
if (!finishedTransfers.Any(t => t.First == transferId)) if (!finishedTransfers.Any(t => t.transferId == transferId))
{ {
GameMain.Client.CancelFileTransfer(transferId); GameMain.Client.CancelFileTransfer(transferId);
DebugConsole.ThrowError("File transfer error: received data without a transfer initiation message"); DebugConsole.ThrowError("File transfer error: received data without a transfer initiation message");
@@ -373,7 +373,7 @@ namespace Barotrauma.Networking
if (ValidateReceivedData(activeTransfer, out string errorMessage)) if (ValidateReceivedData(activeTransfer, out string errorMessage))
{ {
finishedTransfers.Add(new Pair<int, double>(transferId, Timing.TotalTime)); finishedTransfers.Add((transferId, Timing.TotalTime));
StopTransfer(activeTransfer); StopTransfer(activeTransfer);
Md5Hash.RemoveFromCache(activeTransfer.FilePath); Md5Hash.RemoveFromCache(activeTransfer.FilePath);
OnFinished(activeTransfer); OnFinished(activeTransfer);
@@ -391,7 +391,7 @@ namespace Barotrauma.Networking
case (byte)FileTransferMessageType.Cancel: case (byte)FileTransferMessageType.Cancel:
{ {
byte transferId = inc.ReadByte(); byte transferId = inc.ReadByte();
var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId); var matchingTransfer = activeTransfers.Find(t => t.Connection.EndpointMatches(t.Connection.EndPointString) && t.ID == transferId);
if (matchingTransfer != null) if (matchingTransfer != null)
{ {
new GUIMessageBox("File transfer cancelled", "The server has cancelled the transfer of the file \"" + matchingTransfer.FileName + "\"."); new GUIMessageBox("File transfer cancelled", "The server has cancelled the transfer of the file \"" + matchingTransfer.FileName + "\".");
@@ -528,7 +528,7 @@ namespace Barotrauma.Networking
transfer.Status = FileTransferStatus.Canceled; transfer.Status = FileTransferStatus.Canceled;
} }
if (activeTransfers.Contains(transfer)) activeTransfers.Remove(transfer); if (activeTransfers.Contains(transfer)) { activeTransfers.Remove(transfer); }
transfer.Dispose(); transfer.Dispose();
if (deleteFile && File.Exists(transfer.FilePath)) if (deleteFile && File.Exists(transfer.FilePath))
@@ -460,7 +460,7 @@ namespace Barotrauma.Networking
private bool wrongPassword; private bool wrongPassword;
// Before main looping starts, we loop here and wait for approval message // Before main looping starts, we loop here and wait for approval message
private IEnumerable<object> WaitForStartingInfo() private IEnumerable<CoroutineStatus> WaitForStartingInfo()
{ {
GUI.SetCursorWaiting(); GUI.SetCursorWaiting();
requiresPw = false; requiresPw = false;
@@ -861,8 +861,8 @@ namespace Barotrauma.Networking
if (roundInitStatus == RoundInitStatus.WaitingForStartGameFinalize) if (roundInitStatus == RoundInitStatus.WaitingForStartGameFinalize)
{ {
//waiting for a save file //waiting for a save file
if (campaign != null && if (campaign != null &&
campaign.PendingSaveID > campaign.LastSaveID && NetIdUtils.IdMoreRecent(campaign.PendingSaveID, campaign.LastSaveID) &&
fileReceiver.ActiveTransfers.Any(t => t.FileType == FileTransferType.CampaignSave)) fileReceiver.ActiveTransfers.Any(t => t.FileType == FileTransferType.CampaignSave))
{ {
return; return;
@@ -872,6 +872,7 @@ namespace Barotrauma.Networking
break; break;
case ServerPacketHeader.ENDGAME: case ServerPacketHeader.ENDGAME:
CampaignMode.TransitionType transitionType = (CampaignMode.TransitionType)inc.ReadByte(); CampaignMode.TransitionType transitionType = (CampaignMode.TransitionType)inc.ReadByte();
bool save = inc.ReadBoolean();
string endMessage = string.Empty; string endMessage = string.Empty;
endMessage = inc.ReadString(); endMessage = inc.ReadString();
@@ -905,6 +906,7 @@ namespace Barotrauma.Networking
roundInitStatus = RoundInitStatus.Interrupted; roundInitStatus = RoundInitStatus.Interrupted;
CoroutineManager.StartCoroutine(EndGame(endMessage, traitorResults, transitionType), "EndGame"); CoroutineManager.StartCoroutine(EndGame(endMessage, traitorResults, transitionType), "EndGame");
GUI.SetSavingIndicatorState(save);
break; break;
case ServerPacketHeader.CAMPAIGN_SETUP_INFO: case ServerPacketHeader.CAMPAIGN_SETUP_INFO:
UInt16 saveCount = inc.ReadUInt16(); UInt16 saveCount = inc.ReadUInt16();
@@ -1236,7 +1238,7 @@ namespace Barotrauma.Networking
} }
} }
private IEnumerable<object> WaitInServerQueue() private IEnumerable<CoroutineStatus> WaitInServerQueue()
{ {
waitInServerQueueBox = new GUIMessageBox( waitInServerQueueBox = new GUIMessageBox(
TextManager.Get("ServerQueuePleaseWait"), TextManager.Get("ServerQueuePleaseWait"),
@@ -1424,7 +1426,7 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.RefreshEnabledElements(); GameMain.NetLobbyScreen.RefreshEnabledElements();
} }
private IEnumerable<object> StartGame(IReadMessage inc) private IEnumerable<CoroutineStatus> StartGame(IReadMessage inc)
{ {
Character?.Remove(); Character?.Remove();
Character = null; Character = null;
@@ -1562,31 +1564,47 @@ namespace Barotrauma.Networking
if (GameMain.GameSession?.CrewManager != null) { GameMain.GameSession.CrewManager.Reset(); } if (GameMain.GameSession?.CrewManager != null) { GameMain.GameSession.CrewManager.Reset(); }
byte campaignID = inc.ReadByte(); byte campaignID = inc.ReadByte();
UInt16 campaignSaveID = inc.ReadUInt16();
int nextLocationIndex = inc.ReadInt32(); int nextLocationIndex = inc.ReadInt32();
int nextConnectionIndex = inc.ReadInt32(); int nextConnectionIndex = inc.ReadInt32();
int selectedLocationIndex = inc.ReadInt32(); int selectedLocationIndex = inc.ReadInt32();
bool mirrorLevel = inc.ReadBoolean(); bool mirrorLevel = inc.ReadBoolean();
if (campaign.CampaignID != campaignID) if (campaign.CampaignID != campaignID)
{ {
string errorMsg = "Failed to start campaign round (campaign ID does not match).";
gameStarted = true; gameStarted = true;
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError("Failed to start campaign round (campaign ID does not match).");
GameMain.NetLobbyScreen.Select(); GameMain.NetLobbyScreen.Select();
roundInitStatus = RoundInitStatus.Interrupted; roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure; yield return CoroutineStatus.Failure;
} }
else if (campaign.Map == null) else if (campaign.Map == null)
{ {
string errorMsg = "Failed to start campaign round (campaign map not loaded yet).";
gameStarted = true; gameStarted = true;
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError("Failed to start campaign round (campaign map not loaded yet).");
GameMain.NetLobbyScreen.Select(); GameMain.NetLobbyScreen.Select();
roundInitStatus = RoundInitStatus.Interrupted; roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure; yield return CoroutineStatus.Failure;
} }
if (NetIdUtils.IdMoreRecent(campaignSaveID, campaign.PendingSaveID))
{
campaign.PendingSaveID = campaignSaveID;
DateTime saveFileTimeOut = DateTime.Now + new TimeSpan(0,0,60);
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).");
GameMain.NetLobbyScreen.Select();
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
yield return new WaitForSeconds(0.1f);
}
}
campaign.Map.SelectLocation(selectedLocationIndex); campaign.Map.SelectLocation(selectedLocationIndex);
LevelData levelData = nextLocationIndex > -1 ? LevelData levelData = nextLocationIndex > -1 ?
@@ -1665,10 +1683,6 @@ namespace Barotrauma.Networking
} }
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize) { break; } if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize) { break; }
clientPeer.Update((float)Timing.Step);
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize) { break; }
} }
catch (Exception e) catch (Exception e)
{ {
@@ -1744,7 +1758,7 @@ namespace Barotrauma.Networking
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
public IEnumerable<object> EndGame(string endMessage, List<TraitorMissionResult> traitorResults = null, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None) public IEnumerable<CoroutineStatus> EndGame(string endMessage, List<TraitorMissionResult> traitorResults = null, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
{ {
//round starting up, wait for it to finish //round starting up, wait for it to finish
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 60); DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 60);
@@ -1854,8 +1868,7 @@ namespace Barotrauma.Networking
GameMain.GameSession.OwnedSubmarines = new List<SubmarineInfo>(); GameMain.GameSession.OwnedSubmarines = new List<SubmarineInfo>();
for (int i = 0; i < ownedIndexes.Length; i++) for (int i = 0; i < ownedIndexes.Length; i++)
{ {
int index; if (int.TryParse(ownedIndexes[i], out int index))
if (int.TryParse(ownedIndexes[i], out index))
{ {
SubmarineInfo sub = GameMain.Client.ServerSubmarines[index]; SubmarineInfo sub = GameMain.Client.ServerSubmarines[index];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "owned")) if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "owned"))
@@ -2670,8 +2683,11 @@ namespace Barotrauma.Networking
public override void CreateEntityEvent(INetSerializable entity, object[] extraData) public override void CreateEntityEvent(INetSerializable entity, object[] extraData)
{ {
if (!(entity is IClientSerializable)) throw new InvalidCastException("Entity is not IClientSerializable"); if (!(entity is IClientSerializable clientSerializable))
entityEventManager.CreateEvent(entity as IClientSerializable, extraData); {
throw new InvalidCastException($"Entity is not {nameof(IClientSerializable)}");
}
entityEventManager.CreateEvent(clientSerializable, extraData);
} }
public bool HasPermission(ClientPermissions permission) public bool HasPermission(ClientPermissions permission)
@@ -3013,12 +3029,13 @@ namespace Barotrauma.Networking
/// <summary> /// <summary>
/// Tell the server to end the round (permission required) /// Tell the server to end the round (permission required)
/// </summary> /// </summary>
public void RequestRoundEnd() public void RequestRoundEnd(bool save)
{ {
IWriteMessage msg = new WriteOnlyMessage(); IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND); msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
msg.Write((UInt16)ClientPermissions.ManageRound); msg.Write((UInt16)ClientPermissions.ManageRound);
msg.Write(true); //indicates round end msg.Write(true); //indicates round end
msg.Write(save);
clientPeer.Send(msg, DeliveryMethod.Reliable); clientPeer.Send(msg, DeliveryMethod.Reliable);
} }
@@ -3321,7 +3338,9 @@ namespace Barotrauma.Networking
if (respawnManager.RespawnCountdownStarted) if (respawnManager.RespawnCountdownStarted)
{ {
float timeLeft = (float)(respawnManager.RespawnTime - DateTime.Now).TotalSeconds; float timeLeft = (float)(respawnManager.RespawnTime - DateTime.Now).TotalSeconds;
respawnText = TextManager.GetWithVariable(respawnManager.UsingShuttle ? "RespawnShuttleDispatching" : "RespawningIn", "[time]", ToolBox.SecondsToReadableTime(timeLeft)); respawnText = TextManager.GetWithVariable(
respawnManager.UsingShuttle && !respawnManager.ForceSpawnInMainSub ?
"RespawnShuttleDispatching" : "RespawningIn", "[time]", ToolBox.SecondsToReadableTime(timeLeft));
} }
else if (respawnManager.PendingRespawnCount > 0) else if (respawnManager.PendingRespawnCount > 0)
{ {
@@ -3437,7 +3456,7 @@ namespace Barotrauma.Networking
} }
// Need a delayed selection due to the inputbox being deselected when a left click occurs outside of it // Need a delayed selection due to the inputbox being deselected when a left click occurs outside of it
IEnumerable<object> selectCoroutine() IEnumerable<CoroutineStatus> selectCoroutine()
{ {
yield return new WaitForSeconds(0.01f, true); yield return new WaitForSeconds(0.01f, true);
chatBox.InputBox.Select(chatBox.InputBox.Text.Length); chatBox.InputBox.Select(chatBox.InputBox.Text.Length);
@@ -72,6 +72,9 @@ namespace Barotrauma
{ {
CreateLabeledTickBox(parent, nameof(DangerousItemStealBots)); CreateLabeledTickBox(parent, nameof(DangerousItemStealBots));
} }
CreateLabeledSlider(parent, 0.0f, 30.0f, 0.5f, nameof(DangerousItemContainKarmaDecrease));
CreateLabeledTickBox(parent, nameof(IsDangerousItemContainKarmaDecreaseIncremental));
CreateLabeledSlider(parent, 0.0f, 100.0f, 1.0f, nameof(MaxDangerousItemContainKarmaDecrease));
} }
private void CreateLabeledSlider(GUIComponent parent, float min, float max, float step, string propertyName) private void CreateLabeledSlider(GUIComponent parent, float min, float max, float step, string propertyName)
@@ -43,24 +43,9 @@ namespace Barotrauma.Networking
public void CreateEvent(IClientSerializable entity, object[] extraData = null) public void CreateEvent(IClientSerializable entity, object[] extraData = null)
{ {
if (GameMain.Client == null || GameMain.Client.Character == null) return; if (GameMain.Client?.Character == null) { return; }
if (!(entity is Entity)) if (!ValidateEntity(entity)) { return; }
{
DebugConsole.ThrowError("Can't create an entity event for " + entity + "!");
return;
}
if (((Entity)entity).Removed)
{
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the entity has been removed.\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
if (((Entity)entity).IdFreed)
{
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the ID of the entity has been freed.\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
var newEvent = new ClientEntityEvent(entity, (UInt16)(ID + 1)) var newEvent = new ClientEntityEvent(entity, (UInt16)(ID + 1))
{ {
@@ -161,7 +146,7 @@ namespace Barotrauma.Networking
UInt16 firstEventID = msg.ReadUInt16(); UInt16 firstEventID = msg.ReadUInt16();
int eventCount = msg.ReadByte(); int eventCount = msg.ReadByte();
for (int i = 0; i < eventCount; i++) for (int i = 0; i < eventCount; i++)
{ {
//16 = entity ID, 8 = msg length //16 = entity ID, 8 = msg length
@@ -179,7 +164,7 @@ namespace Barotrauma.Networking
UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i); UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i);
UInt16 entityID = msg.ReadUInt16(); UInt16 entityID = msg.ReadUInt16();
if (entityID == Entity.NullEntityID) if (entityID == Entity.NullEntityID)
{ {
if (GameSettings.VerboseLogging) if (GameSettings.VerboseLogging)
@@ -240,8 +225,11 @@ namespace Barotrauma.Networking
if (msg.BitPosition != msgPosition + msgLength * 8) if (msg.BitPosition != msgPosition + msgLength * 8)
{ {
string errorMsg = "Message byte position incorrect after reading an event for the entity \"" + entity.ToString() var prevEntity = entities.Count >= 2 ? entities[entities.Count - 2] : null;
+ "\". Read " + (msg.BitPosition - msgPosition) + " bits, expected message length was " + (msgLength * 8) + " bits."; ushort prevId = prevEntity is Entity p ? p.ID : (ushort)0;
string errorMsg = $"Message byte position incorrect after reading an event for the entity \"{entity}\" (ID {(entity is Entity e ? e.ID : 0)}). "
+$"The previous entity was \"{prevEntity}\" (ID {prevId}) "
+$"Read {msg.BitPosition - msgPosition} bits, expected message length was {msgLength * 8} bits.";
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
@@ -60,11 +60,11 @@ namespace Barotrauma.Networking
{ {
GUI.DrawRectangle(spriteBatch, rect, Color.Black * 0.4f, true); GUI.DrawRectangle(spriteBatch, rect, Color.Black * 0.4f, true);
graphs[(int)NetStatType.ReceivedBytes].Draw(spriteBatch, rect, null, 0.0f, Color.Cyan); graphs[(int)NetStatType.ReceivedBytes].Draw(spriteBatch, rect, color: Color.Cyan);
graphs[(int)NetStatType.SentBytes].Draw(spriteBatch, rect, null, 0.0f, GUI.Style.Orange); graphs[(int)NetStatType.SentBytes].Draw(spriteBatch, rect, null, color: GUI.Style.Orange);
if (graphs[(int)NetStatType.ResentMessages].Average() > 0) if (graphs[(int)NetStatType.ResentMessages].Average() > 0)
{ {
graphs[(int)NetStatType.ResentMessages].Draw(spriteBatch, rect, null, 0.0f, GUI.Style.Red); graphs[(int)NetStatType.ResentMessages].Draw(spriteBatch, rect, color: GUI.Style.Red);
GUI.SmallFont.DrawString(spriteBatch, "Peak resent: " + graphs[(int)NetStatType.ResentMessages].LargestValue() + " messages/s", GUI.SmallFont.DrawString(spriteBatch, "Peak resent: " + graphs[(int)NetStatType.ResentMessages].LargestValue() + " messages/s",
new Vector2(rect.Right + 10, rect.Y + 50), GUI.Style.Red); new Vector2(rect.Right + 10, rect.Y + 50), GUI.Style.Red);
} }
@@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
@@ -17,6 +18,11 @@ namespace Barotrauma.Networking
get; private set; get; private set;
} }
public bool ForceSpawnInMainSub
{
get; private set;
}
partial void UpdateTransportingProjSpecific(float deltaTime) partial void UpdateTransportingProjSpecific(float deltaTime)
{ {
if (GameMain.Client?.Character == null || GameMain.Client.Character.Submarine != RespawnShuttle) { return; } if (GameMain.Client?.Character == null || GameMain.Client.Character.Submarine != RespawnShuttle) { return; }
@@ -30,10 +36,46 @@ namespace Barotrauma.Networking
GameMain.Client.AddChatMessage("ServerMessage.ShuttleLeaving", ChatMessageType.Server); GameMain.Client.AddChatMessage("ServerMessage.ShuttleLeaving", ChatMessageType.Server);
} }
} }
private CoroutineHandle respawnPromptCoroutine;
public void ShowRespawnPromptIfNeeded(float delay = 5.0f)
{
if (!UseRespawnPrompt) { return; }
if (CoroutineManager.IsCoroutineRunning(respawnPromptCoroutine) || GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "respawnquestionprompt"))
{
return;
}
respawnPromptCoroutine = CoroutineManager.Invoke(() =>
{
if (Character.Controlled != null || (!(GameMain.GameSession?.IsRunning ?? false))) { return; }
var respawnPrompt = new GUIMessageBox(
TextManager.Get("tutorial.tryagainheader"), TextManager.Get("respawnquestionprompt"),
new string[] { TextManager.Get("respawnquestionpromptrespawn"), TextManager.Get("respawnquestionpromptwait") })
{
UserData = "respawnquestionprompt"
};
respawnPrompt.Buttons[0].OnClicked += (btn, userdata) =>
{
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: false);
respawnPrompt.Close();
return true;
};
respawnPrompt.Buttons[1].OnClicked += (btn, userdata) =>
{
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: true);
respawnPrompt.Close();
return true;
};
}, delay: delay);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
bool respawnPromptPending = false;
var newState = (State)msg.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length); var newState = (State)msg.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length);
ForceSpawnInMainSub = false;
switch (newState) switch (newState)
{ {
case State.Transporting: case State.Transporting:
@@ -46,13 +88,14 @@ namespace Barotrauma.Networking
if (CurrentState != newState) if (CurrentState != newState)
{ {
CoroutineManager.StopCoroutines("forcepos"); CoroutineManager.StopCoroutines("forcepos");
//CoroutineManager.StartCoroutine(ForceShuttleToPos(Level.Loaded.StartPosition - Vector2.UnitY * Level.ShaftHeight, 100.0f), "forcepos");
} }
break; break;
case State.Waiting: case State.Waiting:
PendingRespawnCount = msg.ReadUInt16(); PendingRespawnCount = msg.ReadUInt16();
RequiredRespawnCount = msg.ReadUInt16(); RequiredRespawnCount = msg.ReadUInt16();
respawnPromptPending = msg.ReadBoolean();
RespawnCountdownStarted = msg.ReadBoolean(); RespawnCountdownStarted = msg.ReadBoolean();
ForceSpawnInMainSub = msg.ReadBoolean();
ResetShuttle(); ResetShuttle();
float newRespawnTime = msg.ReadSingle(); float newRespawnTime = msg.ReadSingle();
RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(newRespawnTime * 1000.0f)); RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(newRespawnTime * 1000.0f));
@@ -63,6 +106,12 @@ namespace Barotrauma.Networking
} }
CurrentState = newState; CurrentState = newState;
if (respawnPromptPending)
{
GameMain.Client.HasSpawned = true;
ShowRespawnPromptIfNeeded(delay: 1.0f);
}
msg.ReadPadBits(); msg.ReadPadBits();
} }
} }
@@ -106,10 +106,10 @@ namespace Barotrauma.Networking
public void CreatePreviewWindow(GUIFrame frame) public void CreatePreviewWindow(GUIFrame frame)
{ {
frame.ClearChildren();
if (frame == null) { return; } if (frame == null) { return; }
frame.ClearChildren();
var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform), ServerName, font: GUI.LargeFont) var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform), ServerName, font: GUI.LargeFont)
{ {
ToolTip = ServerName ToolTip = ServerName
@@ -141,6 +141,10 @@ namespace Barotrauma.Networking
ReadExtraCargo(incMsg); ReadExtraCargo(incMsg);
ReadHiddenSubs(incMsg);
GameMain.NetLobbyScreen.UpdateSubVisibility();
Voting.ClientRead(incMsg); Voting.ClientRead(incMsg);
bool isAdmin = incMsg.ReadBoolean(); bool isAdmin = incMsg.ReadBoolean();
@@ -202,6 +206,11 @@ namespace Barotrauma.Networking
Whitelist.ClientAdminWrite(outMsg); Whitelist.ClientAdminWrite(outMsg);
} }
if (dataToSend.HasFlag(NetFlags.HiddenSubs))
{
WriteHiddenSubs(outMsg);
}
if (dataToSend.HasFlag(NetFlags.Misc)) if (dataToSend.HasFlag(NetFlags.Misc))
{ {
outMsg.WriteRangedInteger(missionTypeOr ?? (int)Barotrauma.MissionType.None, 0, (int)Barotrauma.MissionType.All); outMsg.WriteRangedInteger(missionTypeOr ?? (int)Barotrauma.MissionType.None, 0, (int)Barotrauma.MissionType.All);
@@ -288,7 +297,7 @@ namespace Barotrauma.Networking
}; };
//center frames //center frames
GUIFrame innerFrame = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.8f), settingsFrame.RectTransform, Anchor.Center) { MinSize = new Point(400, 430) }); GUIFrame innerFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.85f), settingsFrame.RectTransform, Anchor.Center) { MinSize = new Point(400, 430) });
GUILayoutGroup paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), innerFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter) GUILayoutGroup paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), innerFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{ {
Stretch = true, Stretch = true,
@@ -363,7 +372,7 @@ namespace Barotrauma.Networking
selectionFrame.RectTransform.NonScaledSize = new Point(selectionFrame.Rect.Width, selectionFrame.Children.First().Rect.Height); selectionFrame.RectTransform.NonScaledSize = new Point(selectionFrame.Rect.Width, selectionFrame.Children.First().Rect.Height);
selectionFrame.RectTransform.IsFixedSize = true; selectionFrame.RectTransform.IsFixedSize = true;
GetPropertyData("SubSelectionMode").AssignGUIComponent(selectionMode); GetPropertyData(nameof(SubSelectionMode)).AssignGUIComponent(selectionMode);
// Mode Selection // Mode Selection
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), serverTab.RectTransform), TextManager.Get("ServerSettingsModeSelection"), font: GUI.SubHeadingFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), serverTab.RectTransform), TextManager.Get("ServerSettingsModeSelection"), font: GUI.SubHeadingFont);
@@ -381,7 +390,7 @@ namespace Barotrauma.Networking
} }
selectionFrame.RectTransform.NonScaledSize = new Point(selectionFrame.Rect.Width, selectionFrame.Children.First().Rect.Height); selectionFrame.RectTransform.NonScaledSize = new Point(selectionFrame.Rect.Width, selectionFrame.Children.First().Rect.Height);
selectionFrame.RectTransform.IsFixedSize = true; selectionFrame.RectTransform.IsFixedSize = true;
GetPropertyData("ModeSelectionMode").AssignGUIComponent(selectionMode); GetPropertyData(nameof(ModeSelectionMode)).AssignGUIComponent(selectionMode);
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), serverTab.RectTransform), style: "HorizontalLine"); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), serverTab.RectTransform), style: "HorizontalLine");
@@ -389,7 +398,7 @@ namespace Barotrauma.Networking
var voiceChatEnabled = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), var voiceChatEnabled = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform),
TextManager.Get("ServerSettingsVoiceChatEnabled")); TextManager.Get("ServerSettingsVoiceChatEnabled"));
GetPropertyData("VoiceChatEnabled").AssignGUIComponent(voiceChatEnabled); GetPropertyData(nameof(VoiceChatEnabled)).AssignGUIComponent(voiceChatEnabled);
//*********************************************** //***********************************************
@@ -407,14 +416,14 @@ namespace Barotrauma.Networking
} }
}; };
startIntervalSlider.Range = new Vector2(10.0f, 300.0f); startIntervalSlider.Range = new Vector2(10.0f, 300.0f);
GetPropertyData("AutoRestartInterval").AssignGUIComponent(startIntervalSlider); GetPropertyData(nameof(AutoRestartInterval)).AssignGUIComponent(startIntervalSlider);
startIntervalSlider.OnMoved(startIntervalSlider, startIntervalSlider.BarScroll); startIntervalSlider.OnMoved(startIntervalSlider, startIntervalSlider.BarScroll);
//*********************************************** //***********************************************
var startWhenClientsReady = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), var startWhenClientsReady = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform),
TextManager.Get("ServerSettingsStartWhenClientsReady")); TextManager.Get("ServerSettingsStartWhenClientsReady"));
GetPropertyData("StartWhenClientsReady").AssignGUIComponent(startWhenClientsReady); GetPropertyData(nameof(StartWhenClientsReady)).AssignGUIComponent(startWhenClientsReady);
CreateLabeledSlider(serverTab, "ServerSettingsStartWhenClientsReadyRatio", out GUIScrollBar slider, out GUITextBlock sliderLabel); CreateLabeledSlider(serverTab, "ServerSettingsStartWhenClientsReadyRatio", out GUIScrollBar slider, out GUITextBlock sliderLabel);
string clientsReadyRequiredLabel = sliderLabel.Text; string clientsReadyRequiredLabel = sliderLabel.Text;
@@ -425,19 +434,19 @@ namespace Barotrauma.Networking
((GUITextBlock)scrollBar.UserData).Text = clientsReadyRequiredLabel.Replace("[percentage]", ((int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f)).ToString()); ((GUITextBlock)scrollBar.UserData).Text = clientsReadyRequiredLabel.Replace("[percentage]", ((int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f)).ToString());
return true; return true;
}; };
GetPropertyData("StartWhenClientsReadyRatio").AssignGUIComponent(slider); GetPropertyData(nameof(StartWhenClientsReadyRatio)).AssignGUIComponent(slider);
slider.OnMoved(slider, slider.BarScroll); slider.OnMoved(slider, slider.BarScroll);
//*********************************************** //***********************************************
var allowSpecBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsAllowSpectating")); var allowSpecBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsAllowSpectating"));
GetPropertyData("AllowSpectating").AssignGUIComponent(allowSpecBox); GetPropertyData(nameof(AllowSpectating)).AssignGUIComponent(allowSpecBox);
var shareSubsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsShareSubFiles")); var shareSubsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsShareSubFiles"));
GetPropertyData("AllowFileTransfers").AssignGUIComponent(shareSubsBox); GetPropertyData(nameof(AllowFileTransfers)).AssignGUIComponent(shareSubsBox);
var randomizeLevelBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsRandomizeSeed")); var randomizeLevelBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsRandomizeSeed"));
GetPropertyData("RandomizeSeed").AssignGUIComponent(randomizeLevelBox); GetPropertyData(nameof(RandomizeSeed)).AssignGUIComponent(randomizeLevelBox);
var saveLogsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsSaveLogs")) var saveLogsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsSaveLogs"))
{ {
@@ -448,7 +457,7 @@ namespace Barotrauma.Networking
return true; return true;
} }
}; };
GetPropertyData("SaveServerLogs").AssignGUIComponent(saveLogsBox); GetPropertyData(nameof(SaveServerLogs)).AssignGUIComponent(saveLogsBox);
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// game settings // game settings
@@ -480,20 +489,20 @@ namespace Barotrauma.Networking
selectionPlayStyle.AddRadioButton((int)playStyle, selectionTick); selectionPlayStyle.AddRadioButton((int)playStyle, selectionTick);
playStyleTickBoxes.Add(selectionTick); playStyleTickBoxes.Add(selectionTick);
} }
GetPropertyData("PlayStyle").AssignGUIComponent(selectionPlayStyle); GetPropertyData(nameof(PlayStyle)).AssignGUIComponent(selectionPlayStyle);
GUITextBlock.AutoScaleAndNormalize(playStyleTickBoxes.Select(t => t.TextBlock)); GUITextBlock.AutoScaleAndNormalize(playStyleTickBoxes.Select(t => t.TextBlock));
playstyleList.RectTransform.MinSize = new Point(0, (int)(playstyleList.Content.Children.First().Rect.Height * 2.0f + playstyleList.Padding.Y + playstyleList.Padding.W)); playstyleList.RectTransform.MinSize = new Point(0, (int)(playstyleList.Content.Children.First().Rect.Height * 2.0f + playstyleList.Padding.Y + playstyleList.Padding.W));
var endVoteBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), var endVoteBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
TextManager.Get("ServerSettingsEndRoundVoting")); TextManager.Get("ServerSettingsEndRoundVoting"));
GetPropertyData("AllowEndVoting").AssignGUIComponent(endVoteBox); GetPropertyData(nameof(AllowEndVoting)).AssignGUIComponent(endVoteBox);
CreateLabeledSlider(roundsTab, "ServerSettingsEndRoundVotesRequired", out slider, out sliderLabel); CreateLabeledSlider(roundsTab, "ServerSettingsEndRoundVotesRequired", out slider, out sliderLabel);
string endRoundLabel = sliderLabel.Text; string endRoundLabel = sliderLabel.Text;
slider.Step = 0.2f; slider.Step = 0.2f;
slider.Range = new Vector2(0.5f, 1.0f); slider.Range = new Vector2(0.5f, 1.0f);
GetPropertyData("EndVoteRequiredRatio").AssignGUIComponent(slider); GetPropertyData(nameof(EndVoteRequiredRatio)).AssignGUIComponent(slider);
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) => slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{ {
((GUITextBlock)scrollBar.UserData).Text = endRoundLabel + " " + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f) + " %"; ((GUITextBlock)scrollBar.UserData).Text = endRoundLabel + " " + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f) + " %";
@@ -503,13 +512,13 @@ namespace Barotrauma.Networking
var respawnBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), var respawnBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
TextManager.Get("ServerSettingsAllowRespawning")); TextManager.Get("ServerSettingsAllowRespawning"));
GetPropertyData("AllowRespawn").AssignGUIComponent(respawnBox); GetPropertyData(nameof(AllowRespawn)).AssignGUIComponent(respawnBox);
CreateLabeledSlider(roundsTab, "ServerSettingsRespawnInterval", out slider, out sliderLabel); CreateLabeledSlider(roundsTab, "ServerSettingsRespawnInterval", out slider, out sliderLabel);
string intervalLabel = sliderLabel.Text; string intervalLabel = sliderLabel.Text;
slider.Range = new Vector2(10.0f, 600.0f); slider.Range = new Vector2(10.0f, 600.0f);
slider.StepValue = 10.0f; slider.StepValue = 10.0f;
GetPropertyData("RespawnInterval").AssignGUIComponent(slider); GetPropertyData(nameof(RespawnInterval)).AssignGUIComponent(slider);
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) => slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{ {
GUITextBlock text = scrollBar.UserData as GUITextBlock; GUITextBlock text = scrollBar.UserData as GUITextBlock;
@@ -518,18 +527,26 @@ namespace Barotrauma.Networking
}; };
slider.OnMoved(slider, slider.BarScroll); slider.OnMoved(slider, slider.BarScroll);
var minRespawnText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), "") var respawnLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), roundsTab.RectTransform),
isHorizontal: true);
var minRespawnLayout
= new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), respawnLayout.RectTransform));
var minRespawnText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), minRespawnLayout.RectTransform), "")
{ {
ToolTip = TextManager.Get("ServerSettingsMinRespawnToolTip") ToolTip = TextManager.Get("ServerSettingsMinRespawnToolTip")
}; };
string minRespawnLabel = TextManager.Get("ServerSettingsMinRespawn") + " "; string minRespawnLabel = TextManager.Get("ServerSettingsMinRespawn") + " ";
CreateLabeledSlider(roundsTab, "", out slider, out sliderLabel); CreateLabeledSlider(minRespawnLayout, "", out slider, out sliderLabel);
sliderLabel.RectTransform.RelativeSize = Vector2.Zero;
slider.RectTransform.RelativeSize = new Vector2(1.0f, 0.5f);
slider.ToolTip = minRespawnText.RawToolTip; slider.ToolTip = minRespawnText.RawToolTip;
slider.UserData = minRespawnText; slider.UserData = minRespawnText;
slider.Step = 0.1f; slider.Step = 0.1f;
slider.Range = new Vector2(0.0f, 1.0f); slider.Range = new Vector2(0.0f, 1.0f);
GetPropertyData("MinRespawnRatio").AssignGUIComponent(slider); GetPropertyData(nameof(MinRespawnRatio)).AssignGUIComponent(slider);
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) => slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{ {
((GUITextBlock)scrollBar.UserData).Text = minRespawnLabel + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f) + " %"; ((GUITextBlock)scrollBar.UserData).Text = minRespawnLabel + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f) + " %";
@@ -537,13 +554,18 @@ namespace Barotrauma.Networking
}; };
slider.OnMoved(slider, MinRespawnRatio); slider.OnMoved(slider, MinRespawnRatio);
var respawnDurationText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), "") var respawnDurationLayout
= new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), respawnLayout.RectTransform));
var respawnDurationText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), respawnDurationLayout.RectTransform), "")
{ {
ToolTip = TextManager.Get("ServerSettingsRespawnDurationToolTip") ToolTip = TextManager.Get("ServerSettingsRespawnDurationToolTip")
}; };
string respawnDurationLabel = TextManager.Get("ServerSettingsRespawnDuration") + " "; string respawnDurationLabel = TextManager.Get("ServerSettingsRespawnDuration") + " ";
CreateLabeledSlider(roundsTab, "", out slider, out sliderLabel); CreateLabeledSlider(respawnDurationLayout, "", out slider, out sliderLabel);
sliderLabel.RectTransform.RelativeSize = Vector2.Zero;
slider.RectTransform.RelativeSize = new Vector2(1.0f, 0.5f);
slider.ToolTip = respawnDurationText.RawToolTip; slider.ToolTip = respawnDurationText.RawToolTip;
slider.UserData = respawnDurationText; slider.UserData = respawnDurationText;
slider.Step = 0.1f; slider.Step = 0.1f;
@@ -556,7 +578,7 @@ namespace Barotrauma.Networking
{ {
return value <= 0.0f ? 1.0f : (value - scrollBar.Range.X) / (scrollBar.Range.Y - scrollBar.Range.X); return value <= 0.0f ? 1.0f : (value - scrollBar.Range.X) / (scrollBar.Range.Y - scrollBar.Range.X);
}; };
GetPropertyData("MaxTransportTime").AssignGUIComponent(slider); GetPropertyData(nameof(MaxTransportTime)).AssignGUIComponent(slider);
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) => slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{ {
if (barScroll == 1.0f) if (barScroll == 1.0f)
@@ -572,14 +594,34 @@ namespace Barotrauma.Networking
}; };
slider.OnMoved(slider, slider.BarScroll); slider.OnMoved(slider, slider.BarScroll);
var losModeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
TextManager.Get("LosEffect"));
var losModeRadioButtonLayout
= new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), roundsTab.RectTransform),
isHorizontal: true)
{
Stretch = true
};
var losModeRadioButtonGroup = new GUIRadioButtonGroup();
LosMode[] losModes = (LosMode[])Enum.GetValues(typeof(LosMode));
for (int i = 0; i < losModes.Length; i++)
{
var losTick = new GUITickBox(new RectTransform(new Vector2(0.3f, 1.0f), losModeRadioButtonLayout.RectTransform), TextManager.Get($"LosMode{losModes[i]}"), font: GUI.SmallFont, style: "GUIRadioButton");
losModeRadioButtonGroup.AddRadioButton(i, losTick);
}
GetPropertyData(nameof(LosMode)).AssignGUIComponent(losModeRadioButtonGroup);
var traitorsMinPlayerCount = CreateLabeledNumberInput(roundsTab, "ServerSettingsTraitorsMinPlayerCount", 1, 16, "ServerSettingsTraitorsMinPlayerCountToolTip"); var traitorsMinPlayerCount = CreateLabeledNumberInput(roundsTab, "ServerSettingsTraitorsMinPlayerCount", 1, 16, "ServerSettingsTraitorsMinPlayerCountToolTip");
GetPropertyData("TraitorsMinPlayerCount").AssignGUIComponent(traitorsMinPlayerCount); GetPropertyData(nameof(TraitorsMinPlayerCount)).AssignGUIComponent(traitorsMinPlayerCount);
var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton")); var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton"));
GetPropertyData("AllowRagdollButton").AssignGUIComponent(ragdollButtonBox); GetPropertyData(nameof(AllowRagdollButton)).AssignGUIComponent(ragdollButtonBox);
var disableBotConversationsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsDisableBotConversations")); var disableBotConversationsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsDisableBotConversations"));
GetPropertyData("DisableBotConversations").AssignGUIComponent(disableBotConversationsBox); GetPropertyData(nameof(DisableBotConversations)).AssignGUIComponent(disableBotConversationsBox);
var buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), roundsTab.RectTransform), isHorizontal: true) var buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), roundsTab.RectTransform), isHorizontal: true)
{ {
@@ -729,35 +771,35 @@ namespace Barotrauma.Networking
var allowFriendlyFire = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform), var allowFriendlyFire = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsAllowFriendlyFire")); TextManager.Get("ServerSettingsAllowFriendlyFire"));
GetPropertyData("AllowFriendlyFire").AssignGUIComponent(allowFriendlyFire); GetPropertyData(nameof(AllowFriendlyFire)).AssignGUIComponent(allowFriendlyFire);
var killableNPCs = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform), var killableNPCs = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsKillableNPCs")); TextManager.Get("ServerSettingsKillableNPCs"));
GetPropertyData("KillableNPCs").AssignGUIComponent(killableNPCs); GetPropertyData(nameof(KillableNPCs)).AssignGUIComponent(killableNPCs);
var destructibleOutposts = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform), var destructibleOutposts = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsDestructibleOutposts")); TextManager.Get("ServerSettingsDestructibleOutposts"));
GetPropertyData("DestructibleOutposts").AssignGUIComponent(destructibleOutposts); GetPropertyData(nameof(DestructibleOutposts)).AssignGUIComponent(destructibleOutposts);
var lockAllDefaultWires = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform), var lockAllDefaultWires = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsLockAllDefaultWires")); TextManager.Get("ServerSettingsLockAllDefaultWires"));
GetPropertyData("LockAllDefaultWires").AssignGUIComponent(lockAllDefaultWires); GetPropertyData(nameof(LockAllDefaultWires)).AssignGUIComponent(lockAllDefaultWires);
var allowRewiring = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform), var allowRewiring = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsAllowRewiring")); TextManager.Get("ServerSettingsAllowRewiring"));
GetPropertyData("AllowRewiring").AssignGUIComponent(allowRewiring); GetPropertyData(nameof(AllowRewiring)).AssignGUIComponent(allowRewiring);
var allowWifiChatter = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform), var allowWifiChatter = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsAllowWifiChat")); TextManager.Get("ServerSettingsAllowWifiChat"));
GetPropertyData("AllowLinkingWifiToChat").AssignGUIComponent(allowWifiChatter); GetPropertyData(nameof(AllowLinkingWifiToChat)).AssignGUIComponent(allowWifiChatter);
var allowDisguises = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform), var allowDisguises = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsAllowDisguises")); TextManager.Get("ServerSettingsAllowDisguises"));
GetPropertyData("AllowDisguises").AssignGUIComponent(allowDisguises); GetPropertyData(nameof(AllowDisguises)).AssignGUIComponent(allowDisguises);
var voteKickBox = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform), var voteKickBox = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsAllowVoteKick")); TextManager.Get("ServerSettingsAllowVoteKick"));
GetPropertyData("AllowVoteKick").AssignGUIComponent(voteKickBox); GetPropertyData(nameof(AllowVoteKick)).AssignGUIComponent(voteKickBox);
GUITextBlock.AutoScaleAndNormalize(tickBoxContainer.Content.Children.Select(c => ((GUITickBox)c).TextBlock)); GUITextBlock.AutoScaleAndNormalize(tickBoxContainer.Content.Children.Select(c => ((GUITickBox)c).TextBlock));
@@ -772,7 +814,7 @@ namespace Barotrauma.Networking
((GUITextBlock)scrollBar.UserData).Text = votesRequiredLabel + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f) + " %"; ((GUITextBlock)scrollBar.UserData).Text = votesRequiredLabel + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f) + " %";
return true; return true;
}; };
GetPropertyData("KickVoteRequiredRatio").AssignGUIComponent(slider); GetPropertyData(nameof(KickVoteRequiredRatio)).AssignGUIComponent(slider);
slider.OnMoved(slider, slider.BarScroll); slider.OnMoved(slider, slider.BarScroll);
CreateLabeledSlider(antigriefingTab, "ServerSettingsAutobanTime", out slider, out sliderLabel); CreateLabeledSlider(antigriefingTab, "ServerSettingsAutobanTime", out slider, out sliderLabel);
@@ -784,13 +826,13 @@ namespace Barotrauma.Networking
((GUITextBlock)scrollBar.UserData).Text = autobanLabel + ToolBox.SecondsToReadableTime(scrollBar.BarScrollValue); ((GUITextBlock)scrollBar.UserData).Text = autobanLabel + ToolBox.SecondsToReadableTime(scrollBar.BarScrollValue);
return true; return true;
}; };
GetPropertyData("AutoBanTime").AssignGUIComponent(slider); GetPropertyData(nameof(AutoBanTime)).AssignGUIComponent(slider);
slider.OnMoved(slider, slider.BarScroll); slider.OnMoved(slider, slider.BarScroll);
var wrongPasswordBanBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), antigriefingTab.RectTransform), TextManager.Get("ServerSettingsBanAfterWrongPassword")); var wrongPasswordBanBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), antigriefingTab.RectTransform), TextManager.Get("ServerSettingsBanAfterWrongPassword"));
GetPropertyData("BanAfterWrongPassword").AssignGUIComponent(wrongPasswordBanBox); GetPropertyData(nameof(BanAfterWrongPassword)).AssignGUIComponent(wrongPasswordBanBox);
var allowedPasswordRetries = CreateLabeledNumberInput(antigriefingTab, "ServerSettingsPasswordRetriesBeforeBan", 0, 10); var allowedPasswordRetries = CreateLabeledNumberInput(antigriefingTab, "ServerSettingsPasswordRetriesBeforeBan", 0, 10);
GetPropertyData("MaxPasswordRetriesBeforeBan").AssignGUIComponent(allowedPasswordRetries); GetPropertyData(nameof(MaxPasswordRetriesBeforeBan)).AssignGUIComponent(allowedPasswordRetries);
wrongPasswordBanBox.OnSelected += (tb) => wrongPasswordBanBox.OnSelected += (tb) =>
{ {
allowedPasswordRetries.Enabled = tb.Selected; allowedPasswordRetries.Enabled = tb.Selected;
@@ -800,7 +842,7 @@ namespace Barotrauma.Networking
// karma -------------------------------------------------------------------------- // karma --------------------------------------------------------------------------
var karmaBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), antigriefingTab.RectTransform), TextManager.Get("ServerSettingsUseKarma")); var karmaBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), antigriefingTab.RectTransform), TextManager.Get("ServerSettingsUseKarma"));
GetPropertyData("KarmaEnabled").AssignGUIComponent(karmaBox); GetPropertyData(nameof(KarmaEnabled)).AssignGUIComponent(karmaBox);
karmaPresetDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), antigriefingTab.RectTransform)); karmaPresetDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), antigriefingTab.RectTransform));
foreach (string karmaPreset in GameMain.NetworkMember.KarmaManager.Presets.Keys) foreach (string karmaPreset in GameMain.NetworkMember.KarmaManager.Presets.Keys)
@@ -935,7 +935,7 @@ namespace Barotrauma.Steam
return workshopPublishStatus; return workshopPublishStatus;
} }
private static IEnumerable<object> PublishItem(WorkshopPublishStatus workshopPublishStatus) private static IEnumerable<CoroutineStatus> PublishItem(WorkshopPublishStatus workshopPublishStatus)
{ {
if (!isInitialized) if (!isInitialized)
{ {
@@ -66,10 +66,10 @@ namespace Barotrauma
if (clients == null) { return; } if (clients == null) { return; }
List<Pair<object, int>> voteList = GetVoteList(voteType, clients); IReadOnlyDictionary<object, int> voteList = GetVoteCounts<object>(voteType, clients);
foreach (Pair<object, int> votable in voteList) foreach (KeyValuePair<object, int> votable in voteList)
{ {
SetVoteText(listBox, votable.First, votable.Second); SetVoteText(listBox, votable.Key, votable.Value);
} }
break; break;
case VoteType.StartRound: case VoteType.StartRound:
@@ -106,8 +106,12 @@ namespace Barotrauma.Particles
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null) public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
{ {
this.prefab = prefab; this.prefab = prefab;
#if DEBUG
debugName = $"Particle ({prefab.Name})"; debugName = $"Particle ({prefab.Name})";
#else
//don't instantiate new string objects in release builds
debugName = prefab.Name;
#endif
spriteIndex = Rand.Int(prefab.Sprites.Count); spriteIndex = Rand.Int(prefab.Sprites.Count);
animState = 0; animState = 0;
@@ -188,11 +188,14 @@ namespace Barotrauma.Particles
var particlePrefab = overrideParticle ?? Prefab.ParticlePrefab; var particlePrefab = overrideParticle ?? Prefab.ParticlePrefab;
if (particlePrefab == null) { return; } if (particlePrefab == null) { return; }
angle += Rand.Range(Prefab.Properties.AngleMinRad, Prefab.Properties.AngleMaxRad); Vector2 velocity = Vector2.Zero;
if (!MathUtils.NearlyEqual(Prefab.Properties.VelocityMax * velocityMultiplier, 0.0f) || !MathUtils.NearlyEqual(Prefab.Properties.DistanceMax, 0.0f))
Vector2 dir = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)); {
Vector2 velocity = dir * Rand.Range(Prefab.Properties.VelocityMin, Prefab.Properties.VelocityMax) * velocityMultiplier; angle += Rand.Range(Prefab.Properties.AngleMinRad, Prefab.Properties.AngleMaxRad);
position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax); Vector2 dir = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
velocity = dir * Rand.Range(Prefab.Properties.VelocityMin, Prefab.Properties.VelocityMax) * velocityMultiplier;
position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax);
}
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop, tracerPoints: tracerPoints); var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop, tracerPoints: tracerPoints);
@@ -167,8 +167,8 @@ namespace Barotrauma.Particles
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; } if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; } if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
if (particles[particleCount] == null) particles[particleCount] = new Particle(); if (particles[particleCount] == null) { particles[particleCount] = new Particle(); }
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer, tracerPoints: tracerPoints); particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer, tracerPoints: tracerPoints);
@@ -255,7 +255,7 @@ namespace Barotrauma
File.WriteAllText(filePath, crashReport); File.WriteAllText(filePath, crashReport);
if (GameSettings.SaveDebugConsoleLogs) DebugConsole.SaveLogs(); if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); }
if (GameSettings.SendUserStatistics) if (GameSettings.SendUserStatistics)
{ {
@@ -271,7 +271,7 @@ namespace Barotrauma
} }
} }
private IEnumerable<object> WaitForCampaignSetup() private IEnumerable<CoroutineStatus> WaitForCampaignSetup()
{ {
GUI.SetCursorWaiting(); GUI.SetCursorWaiting();
string headerText = TextManager.Get("CampaignStartingPleaseWait"); string headerText = TextManager.Get("CampaignStartingPleaseWait");
@@ -1802,9 +1802,11 @@ namespace Barotrauma.CharacterEditor
{ {
case AnimationType.Walk: case AnimationType.Walk:
case AnimationType.Run: case AnimationType.Run:
case AnimationType.Crouch:
if (!ragdollParams.CanWalk) { continue; } if (!ragdollParams.CanWalk) { continue; }
break; break;
case AnimationType.Crouch:
if (!ragdollParams.CanWalk || !isHumanoid) { continue; }
break;
case AnimationType.SwimSlow: case AnimationType.SwimSlow:
case AnimationType.SwimFast: case AnimationType.SwimFast:
break; break;
@@ -2690,7 +2692,15 @@ namespace Barotrauma.CharacterEditor
characterDropDown.SelectItem(currentCharacterConfig); characterDropDown.SelectItem(currentCharacterConfig);
characterDropDown.OnSelected = (component, data) => characterDropDown.OnSelected = (component, data) =>
{ {
SpawnCharacter((string)data); string configFile = (string)data;
try
{
SpawnCharacter(configFile);
}
catch (Exception e)
{
HandleSpawnException(configFile, e);
}
return true; return true;
}; };
if (currentCharacterConfig == CharacterPrefab.HumanConfigFile) if (currentCharacterConfig == CharacterPrefab.HumanConfigFile)
@@ -2719,19 +2729,48 @@ namespace Barotrauma.CharacterEditor
prevCharacterButton.TextBlock.AutoScaleHorizontal = true; prevCharacterButton.TextBlock.AutoScaleHorizontal = true;
prevCharacterButton.OnClicked += (b, obj) => prevCharacterButton.OnClicked += (b, obj) =>
{ {
SpawnCharacter(GetPreviousConfigFile()); string configFile = GetPreviousConfigFile();
try
{
SpawnCharacter(configFile);
}
catch (Exception e)
{
HandleSpawnException(configFile, e);
}
return true; return true;
}; };
var nextCharacterButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), charButtons.RectTransform, Anchor.TopRight), GetCharacterEditorTranslation("NextCharacter")); var nextCharacterButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), charButtons.RectTransform, Anchor.TopRight), GetCharacterEditorTranslation("NextCharacter"));
prevCharacterButton.TextBlock.AutoScaleHorizontal = true; prevCharacterButton.TextBlock.AutoScaleHorizontal = true;
nextCharacterButton.OnClicked += (b, obj) => nextCharacterButton.OnClicked += (b, obj) =>
{ {
SpawnCharacter(GetNextConfigFile()); string configFile = GetNextConfigFile();
try
{
SpawnCharacter(configFile);
}
catch (Exception e)
{
HandleSpawnException(configFile, e);
}
return true; return true;
}; };
charButtons.RectTransform.MinSize = new Point(0, prevCharacterButton.RectTransform.MinSize.Y); charButtons.RectTransform.MinSize = new Point(0, prevCharacterButton.RectTransform.MinSize.Y);
characterPanelToggle = new ToggleButton(new RectTransform(new Vector2(0.08f, 1), characterSelectionPanel.RectTransform, Anchor.CenterLeft, Pivot.CenterRight), Direction.Right); characterPanelToggle = new ToggleButton(new RectTransform(new Vector2(0.08f, 1), characterSelectionPanel.RectTransform, Anchor.CenterLeft, Pivot.CenterRight), Direction.Right);
characterSelectionPanel.RectTransform.MinSize = new Point(0, (int)(content.RectTransform.Children.Sum(c => c.MinSize.Y) * 1.2f)); characterSelectionPanel.RectTransform.MinSize = new Point(0, (int)(content.RectTransform.Children.Sum(c => c.MinSize.Y) * 1.2f));
void HandleSpawnException(string configFile, Exception e)
{
if (configFile != CharacterPrefab.HumanConfigFile)
{
DebugConsole.ThrowError($"Failed to spawn the character \"{configFile}\".", e);
SpawnCharacter(CharacterPrefab.HumanConfigFile);
}
else
{
throw new Exception($"Failed to spawn the character \"{configFile}\".", innerException: e);
}
}
} }
private void CreateFileEditPanel() private void CreateFileEditPanel()
@@ -143,9 +143,33 @@ namespace Barotrauma
editorContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedRightPanel.RectTransform)); editorContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedRightPanel.RectTransform));
var seedContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.02f), paddedRightPanel.RectTransform), isHorizontal: true); var seedContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), paddedRightPanel.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), seedContainer.RectTransform), TextManager.Get("leveleditor.levelseed")); Vector2 randomizeButtonRelativeSize = GetRandomizeButtonRelativeSize();
seedBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), seedContainer.RectTransform), ToolBox.RandomSeed(8)); Vector2 elementRelativeSize = GetSeedElementRelativeSize();
var seedLabel = new GUITextBlock(new RectTransform(elementRelativeSize, seedContainer.RectTransform), TextManager.Get("leveleditor.levelseed"));
seedBox = new GUITextBox(new RectTransform(elementRelativeSize, seedContainer.RectTransform), GetLevelSeed());
var seedButton = new GUIButton(new RectTransform(randomizeButtonRelativeSize, seedContainer.RectTransform), style: "RandomizeButton")
{
OnClicked = (button, userData) =>
{
if(seedBox == null) { return false; }
seedBox.Text = GetLevelSeed();
return true;
}
};
seedContainer.RectTransform.SizeChanged += () =>
{
Vector2 randomizeButtonRelativeSize = GetRandomizeButtonRelativeSize();
Vector2 elementRelativeSize = GetSeedElementRelativeSize();
seedLabel.RectTransform.RelativeSize = elementRelativeSize;
seedBox.RectTransform.RelativeSize = elementRelativeSize;
seedButton.RectTransform.RelativeSize = randomizeButtonRelativeSize;
};
Vector2 GetRandomizeButtonRelativeSize() => 0.2f * seedContainer.Rect.Width > seedContainer.Rect.Height ?
new Vector2(Math.Min((float)seedContainer.Rect.Height / seedContainer.Rect.Width, 0.2f), 1.0f) :
new Vector2(0.15f, Math.Min((0.2f * seedContainer.Rect.Width) / seedContainer.Rect.Height, 1.0f));
Vector2 GetSeedElementRelativeSize() => new Vector2(0.5f * (1.0f - randomizeButtonRelativeSize.X), 1.0f);
static string GetLevelSeed() => ToolBox.RandomSeed(8);
mirrorLevel = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.02f), paddedRightPanel.RectTransform), TextManager.Get("mirrorentityx")); mirrorLevel = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.02f), paddedRightPanel.RectTransform), TextManager.Get("mirrorentityx"));
@@ -679,7 +679,7 @@ namespace Barotrauma
return true; return true;
} }
private IEnumerable<object> SelectScreenWithWaitCursor(Screen screen) private IEnumerable<CoroutineStatus> SelectScreenWithWaitCursor(Screen screen)
{ {
GUI.SetCursorWaiting(); GUI.SetCursorWaiting();
//tiny delay to get the cursor to render //tiny delay to get the cursor to render
@@ -719,7 +719,7 @@ namespace Barotrauma
} }
#endregion #endregion
public void QuickStart(bool fixedSeed = false, string sub = null) public void QuickStart(bool fixedSeed = false, string sub = null, float difficulty = 40, LevelGenerationParams levelGenerationParams = null)
{ {
if (fixedSeed) if (fixedSeed)
{ {
@@ -751,7 +751,7 @@ namespace Barotrauma
GameModePreset.DevSandbox, GameModePreset.DevSandbox,
missionPrefabs: null); missionPrefabs: null);
//(gamesession.GameMode as SinglePlayerCampaign).GenerateMap(ToolBox.RandomSeed(8)); //(gamesession.GameMode as SinglePlayerCampaign).GenerateMap(ToolBox.RandomSeed(8));
gamesession.StartRound(fixedSeed ? "abcd" : ToolBox.RandomSeed(8), difficulty: 40); gamesession.StartRound(fixedSeed ? "abcd" : ToolBox.RandomSeed(8), difficulty, levelGenerationParams);
GameMain.GameScreen.Select(); GameMain.GameScreen.Select();
// TODO: modding support // TODO: modding support
string[] jobIdentifiers = new string[] { "captain", "engineer", "mechanic", "securityofficer", "medicaldoctor" }; string[] jobIdentifiers = new string[] { "captain", "engineer", "mechanic", "securityofficer", "medicaldoctor" };
@@ -890,7 +890,7 @@ namespace Barotrauma
} }
} }
private IEnumerable<object> WaitForSubmarineHashCalculations(GUIMessageBox messageBox) private IEnumerable<CoroutineStatus> WaitForSubmarineHashCalculations(GUIMessageBox messageBox)
{ {
string originalText = messageBox.Text.Text; string originalText = messageBox.Text.Text;
int doneCount = 0; int doneCount = 0;
@@ -1452,7 +1452,7 @@ namespace Barotrauma
} }
} }
private IEnumerable<object> WairForRemoteContentReceived() private IEnumerable<CoroutineStatus> WairForRemoteContentReceived()
{ {
while (true) while (true)
{ {
@@ -18,9 +18,7 @@ namespace Barotrauma
private readonly GUILayoutGroup infoFrameContent; private readonly GUILayoutGroup infoFrameContent;
private readonly GUIFrame myCharacterFrame; private readonly GUIFrame myCharacterFrame;
private readonly GUIListBox subList, modeList; private readonly GUIListBox chatBox;
private readonly GUIListBox chatBox, playerList;
private readonly GUIButton serverLogReverseButton; private readonly GUIButton serverLogReverseButton;
private readonly GUIListBox serverLogBox, serverLogFilterTicks; private readonly GUIListBox serverLogBox, serverLogFilterTicks;
@@ -73,7 +71,8 @@ namespace Barotrauma
private readonly GUIComponent gameModeContainer; private readonly GUIComponent gameModeContainer;
private readonly GUIButton spectateButton; private readonly GUIButton spectateButton;
private readonly GUILayoutGroup roundControlsHolder; private readonly GUILayoutGroup roundControlsHolder;
public GUIButton SettingsButton { get; private set; }
public readonly GUIButton SettingsButton;
public static GUIButton JobInfoFrame; public static GUIButton JobInfoFrame;
private readonly GUITickBox spectateBox; private readonly GUITickBox spectateBox;
@@ -85,12 +84,15 @@ namespace Barotrauma
private bool createPendingChangesText = true; private bool createPendingChangesText = true;
public GUIButton PlayerFrame; public GUIButton PlayerFrame;
public readonly GUIButton SubVisibilityButton;
private readonly GUITextBox subSearchBox;
private readonly GUIComponent subPreviewContainer; private readonly GUIComponent subPreviewContainer;
private readonly GUITickBox autoRestartBox; private readonly GUITickBox autoRestartBox;
private readonly GUITextBlock autoRestartText; private readonly GUITextBlock autoRestartText;
private readonly GUIDropDown shuttleList;
private readonly GUITickBox shuttleTickBox; private readonly GUITickBox shuttleTickBox;
private readonly GUIComponent settingsBlocker; private readonly GUIComponent settingsBlocker;
@@ -161,20 +163,11 @@ namespace Barotrauma
private readonly GUITextBlock publicOrPrivate; private readonly GUITextBlock publicOrPrivate;
public GUIListBox SubList public readonly GUIListBox SubList;
{
get { return subList; }
}
public GUIDropDown ShuttleList public readonly GUIDropDown ShuttleList;
{
get { return shuttleList; }
}
public GUIListBox ModeList public readonly GUIListBox ModeList;
{
get { return modeList; }
}
private int selectedModeIndex; private int selectedModeIndex;
public int SelectedModeIndex public int SelectedModeIndex
@@ -184,7 +177,7 @@ namespace Barotrauma
{ {
if (HighlightedModeIndex == selectedModeIndex) if (HighlightedModeIndex == selectedModeIndex)
{ {
modeList.Select(value); ModeList.Select(value);
} }
selectedModeIndex = value; selectedModeIndex = value;
} }
@@ -192,17 +185,14 @@ namespace Barotrauma
public int HighlightedModeIndex public int HighlightedModeIndex
{ {
get { return modeList.SelectedIndex; } get { return ModeList.SelectedIndex; }
set set
{ {
modeList.Select(value, true); ModeList.Select(value, true);
} }
} }
public GUIListBox PlayerList public readonly GUIListBox PlayerList;
{
get { return playerList; }
}
public GUITextBox CharacterNameBox public GUITextBox CharacterNameBox
{ {
@@ -228,16 +218,9 @@ namespace Barotrauma
private set; private set;
} }
public SubmarineInfo SelectedSub public SubmarineInfo SelectedSub => SubList.SelectedData as SubmarineInfo;
{
get { return subList.SelectedData as SubmarineInfo; }
set { subList.Select(value); }
}
public SubmarineInfo SelectedShuttle public SubmarineInfo SelectedShuttle => ShuttleList.SelectedData as SubmarineInfo;
{
get { return shuttleList.SelectedData as SubmarineInfo; }
}
public MultiPlayerCampaignSetupUI CampaignSetupUI; public MultiPlayerCampaignSetupUI CampaignSetupUI;
public List<SubmarineInfo> CampaignSubmarines = new List<SubmarineInfo>(); public List<SubmarineInfo> CampaignSubmarines = new List<SubmarineInfo>();
@@ -253,7 +236,7 @@ namespace Barotrauma
public GameModePreset SelectedMode public GameModePreset SelectedMode
{ {
get { return modeList.SelectedData as GameModePreset; } get { return ModeList.SelectedData as GameModePreset; }
} }
public MissionType MissionType public MissionType MissionType
@@ -529,7 +512,7 @@ namespace Barotrauma
//player list ------------------------------------------------------------------ //player list ------------------------------------------------------------------
playerList = new GUIListBox(new RectTransform(new Vector2(0.4f, 1.0f), socialHolderHorizontal.RectTransform)) PlayerList = new GUIListBox(new RectTransform(new Vector2(0.4f, 1.0f), socialHolderHorizontal.RectTransform))
{ {
OnSelected = (component, userdata) => { SelectPlayer(userdata as Client); return true; } OnSelected = (component, userdata) => { SelectPlayer(userdata as Client); return true; }
}; };
@@ -737,25 +720,65 @@ namespace Barotrauma
}; };
var serverMessageContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.75f), serverInfoHolder.RectTransform)); var serverMessageContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.75f), serverInfoHolder.RectTransform));
ServerMessage = new GUITextBox(new RectTransform(Vector2.One, serverMessageContainer.Content.RectTransform), style: "GUITextBoxNoBorder") ServerMessage = new GUITextBox(new RectTransform(Vector2.One, serverMessageContainer.Content.RectTransform),
style: "GUITextBoxNoBorder", wrap: true, textAlignment: Alignment.TopLeft);
var serverMessageHint = new GUITextBlock(new RectTransform(Vector2.One, ServerMessage.RectTransform),
textColor: Color.DarkGray * 0.6f, textAlignment: Alignment.TopLeft, font: GUI.Style.Font, text: TextManager.Get("ClickToWriteServerMessage"));
void updateServerMessageScrollBasedOnCaret()
{ {
Wrap = true float caretY = ServerMessage.CaretScreenPos.Y;
float bottomCaretExtent = ServerMessage.Font.LineHeight * 1.5f;
float topCaretExtent = -ServerMessage.Font.LineHeight * 0.5f;
if (caretY + bottomCaretExtent > serverMessageContainer.Rect.Bottom)
{
serverMessageContainer.ScrollBar.BarScroll
= (caretY - ServerMessage.Rect.Top - serverMessageContainer.Rect.Height + bottomCaretExtent)
/ (ServerMessage.Rect.Height - serverMessageContainer.Rect.Height);
}
else if (caretY + topCaretExtent < serverMessageContainer.Rect.Top)
{
serverMessageContainer.ScrollBar.BarScroll
= (caretY - ServerMessage.Rect.Top + topCaretExtent)
/ (ServerMessage.Rect.Height - serverMessageContainer.Rect.Height);
}
}
ServerMessage.OnSelected += (textBox, key) =>
{
serverMessageHint.Visible = false;
updateServerMessageScrollBasedOnCaret();
}; };
ServerMessage.OnTextChanged += (textBox, text) => ServerMessage.OnTextChanged += (textBox, text) =>
{ {
Vector2 textSize = textBox.Font.MeasureString(textBox.WrappedText); 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)); textBox.RectTransform.NonScaledSize = new Point(textBox.RectTransform.NonScaledSize.X, Math.Max(serverMessageContainer.Content.Rect.Height, (int)textSize.Y + 10));
serverMessageContainer.UpdateScrollBarSize(); serverMessageContainer.UpdateScrollBarSize();
serverMessageContainer.BarScroll = 1.0f; serverMessageHint.Visible = !textBox.Selected && !textBox.Readonly && string.IsNullOrWhiteSpace(textBox.Text);
return true;
};
ServerMessage.OnEnterPressed += (textBox, text) =>
{
string str = textBox.Text;
int caretIndex = textBox.CaretIndex;
textBox.Text = $"{str[..caretIndex]}\n{str[caretIndex..]}";
textBox.CaretIndex = caretIndex + 1;
return true; return true;
}; };
ServerMessage.OnDeselected += (textBox, key) => ServerMessage.OnDeselected += (textBox, key) =>
{ {
if (!textBox.Readonly) if (!textBox.Readonly)
{ {
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Message); GameMain.Client?.ServerSettings?.ClientAdminWrite(ServerSettings.NetFlags.Message);
} }
serverMessageHint.Visible = !textBox.Readonly && string.IsNullOrWhiteSpace(textBox.Text);
}; };
ServerMessage.OnKeyHit += (sender, key) => updateServerMessageScrollBasedOnCaret();
clientHiddenElements.Add(serverMessageHint);
clientReadonlyElements.Add(ServerMessage); clientReadonlyElements.Add(ServerMessage);
//submarine list ------------------------------------------------------------------ //submarine list ------------------------------------------------------------------
@@ -768,26 +791,36 @@ namespace Barotrauma
var subLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.055f), subHolder.RectTransform) { MinSize = new Point(0, 25) }, TextManager.Get("Submarine"), font: GUI.SubHeadingFont); var subLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.055f), subHolder.RectTransform) { MinSize = new Point(0, 25) }, TextManager.Get("Submarine"), font: GUI.SubHeadingFont);
SubVisibilityButton
= new GUIButton(
new RectTransform(Vector2.One * 1.2f, subLabel.RectTransform, anchor: Anchor.CenterRight,
scaleBasis: ScaleBasis.BothHeight),
style: "EyeButton")
{
OnClicked = (button, o) =>
{
CreateSubmarineVisibilityMenu();
return false;
}
};
clientHiddenElements.Add(SubVisibilityButton);
var filterContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subHolder.RectTransform), isHorizontal: true) var filterContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subHolder.RectTransform), isHorizontal: true)
{ {
Stretch = true Stretch = true
}; };
var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("serverlog.filter"), textAlignment: Alignment.CenterLeft, font: GUI.Font); var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("serverlog.filter"), textAlignment: Alignment.CenterLeft, font: GUI.Font);
var searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform, Anchor.CenterRight), font: GUI.Font, createClearButton: true); subSearchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform, Anchor.CenterRight), font: GUI.Font, createClearButton: true);
filterContainer.RectTransform.MinSize = searchBox.RectTransform.MinSize; filterContainer.RectTransform.MinSize = subSearchBox.RectTransform.MinSize;
searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; }; subSearchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; }; subSearchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };
searchBox.OnTextChanged += (textBox, text) => subSearchBox.OnTextChanged += (textBox, text) =>
{ {
foreach (GUIComponent child in subList.Content.Children) UpdateSubVisibility();
{
if (!(child.UserData is SubmarineInfo sub)) { continue; }
child.Visible = string.IsNullOrEmpty(text) || sub.DisplayName.ToLower().Contains(text.ToLower());
}
return true; return true;
}; };
subList = new GUIListBox(new RectTransform(Vector2.One, subHolder.RectTransform)) SubList = new GUIListBox(new RectTransform(Vector2.One, subHolder.RectTransform))
{ {
OnSelected = VotableClicked OnSelected = VotableClicked
}; };
@@ -832,7 +865,7 @@ namespace Barotrauma
shuttleTickBox.TextBlock.TextScale = 1.0f; shuttleTickBox.TextBlock.TextScale = 1.0f;
} }
}; };
shuttleList = new GUIDropDown(new RectTransform(Vector2.One, shuttleHolder.RectTransform), elementCount: 10) ShuttleList = new GUIDropDown(new RectTransform(Vector2.One, shuttleHolder.RectTransform), elementCount: 10)
{ {
OnSelected = (component, obj) => OnSelected = (component, obj) =>
{ {
@@ -840,8 +873,8 @@ namespace Barotrauma
return true; return true;
} }
}; };
shuttleList.ListBox.RectTransform.MinSize = new Point(250, 0); ShuttleList.ListBox.RectTransform.MinSize = new Point(250, 0);
shuttleHolder.RectTransform.MinSize = new Point(0, shuttleList.RectTransform.Children.Max(c => c.MinSize.Y)); shuttleHolder.RectTransform.MinSize = new Point(0, ShuttleList.RectTransform.Children.Max(c => c.MinSize.Y));
subPreviewContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), rightColumn.RectTransform), style: null); subPreviewContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), rightColumn.RectTransform), style: null);
subPreviewContainer.RectTransform.SizeChanged += () => subPreviewContainer.RectTransform.SizeChanged += () =>
@@ -871,7 +904,7 @@ namespace Barotrauma
UserData = "modevotes", UserData = "modevotes",
Visible = false Visible = false
}; };
modeList = new GUIListBox(new RectTransform(Vector2.One, gameModeHolder.RectTransform)) ModeList = new GUIListBox(new RectTransform(Vector2.One, gameModeHolder.RectTransform))
{ {
OnSelected = VotableClicked OnSelected = VotableClicked
}; };
@@ -880,7 +913,7 @@ namespace Barotrauma
{ {
if (mode.IsSinglePlayer) { continue; } if (mode.IsSinglePlayer) { continue; }
var modeFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), modeList.Content.RectTransform), style: null) var modeFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), ModeList.Content.RectTransform), style: null)
{ {
UserData = mode UserData = mode
}; };
@@ -938,7 +971,7 @@ namespace Barotrauma
{ {
OnClicked = (_, __) => OnClicked = (_, __) =>
{ {
GameMain.Client.RequestSelectMode(modeList.Content.GetChildIndex(modeList.Content.GetChildByUserData(GameModePreset.Sandbox))); GameMain.Client.RequestSelectMode(ModeList.Content.GetChildIndex(ModeList.Content.GetChildByUserData(GameModePreset.Sandbox)));
return true; return true;
} }
}; };
@@ -1221,7 +1254,7 @@ namespace Barotrauma
GUI.ClearCursorWait(); GUI.ClearCursorWait();
} }
public IEnumerable<object> WaitForStartRound(GUIButton startButton) public IEnumerable<CoroutineStatus> WaitForStartRound(GUIButton startButton)
{ {
GUI.SetCursorWaiting(); GUI.SetCursorWaiting();
string headerText = TextManager.Get("RoundStartingPleaseWait"); string headerText = TextManager.Get("RoundStartingPleaseWait");
@@ -1264,6 +1297,8 @@ namespace Barotrauma
{ {
if (GameMain.NetworkMember == null) { return; } if (GameMain.NetworkMember == null) { return; }
visibilityMenuOrder.Clear();
CharacterAppearanceCustomizationMenu?.Dispose(); CharacterAppearanceCustomizationMenu?.Dispose();
JobSelectionFrame = null; JobSelectionFrame = null;
@@ -1362,7 +1397,7 @@ namespace Barotrauma
ServerMessage.Readonly = !GameMain.Client.HasPermission(ClientPermissions.ManageSettings); ServerMessage.Readonly = !GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
shuttleTickBox.Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings); shuttleTickBox.Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
SubList.Enabled = !CampaignFrame.Visible && (GameMain.Client.ServerSettings.Voting.AllowSubVoting || GameMain.Client.HasPermission(ClientPermissions.SelectSub)); SubList.Enabled = !CampaignFrame.Visible && (GameMain.Client.ServerSettings.Voting.AllowSubVoting || GameMain.Client.HasPermission(ClientPermissions.SelectSub));
shuttleList.Enabled = shuttleList.ButtonEnabled = GameMain.Client.HasPermission(ClientPermissions.SelectSub); ShuttleList.Enabled = ShuttleList.ButtonEnabled = GameMain.Client.HasPermission(ClientPermissions.SelectSub);
ModeList.Enabled = GameMain.Client.ServerSettings.Voting.AllowModeVoting || GameMain.Client.HasPermission(ClientPermissions.SelectMode); ModeList.Enabled = GameMain.Client.ServerSettings.Voting.AllowModeVoting || GameMain.Client.HasPermission(ClientPermissions.SelectMode);
LogButtons.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog); LogButtons.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog);
GameMain.Client.ShowLogButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog); GameMain.Client.ShowLogButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog);
@@ -1370,6 +1405,8 @@ namespace Barotrauma
roundControlsHolder.Children.ForEach(c => c.RectTransform.RelativeSize = Vector2.One); roundControlsHolder.Children.ForEach(c => c.RectTransform.RelativeSize = Vector2.One);
roundControlsHolder.Recalculate(); roundControlsHolder.Recalculate();
SubVisibilityButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
ReadyToStartBox.Parent.Visible = !GameMain.Client.GameStarted; ReadyToStartBox.Parent.Visible = !GameMain.Client.GameStarted;
RefreshGameModeContent(); RefreshGameModeContent();
@@ -1557,7 +1594,7 @@ namespace Barotrauma
}; };
} }
UpdateJobPreferences(); UpdateJobPreferences(characterInfo);
appearanceFrame = new GUIFrame(new RectTransform(Vector2.One, characterInfoFrame.RectTransform), style: "GUIFrameListBox") appearanceFrame = new GUIFrame(new RectTransform(Vector2.One, characterInfoFrame.RectTransform), style: "GUIFrameListBox")
{ {
@@ -1853,7 +1890,7 @@ namespace Barotrauma
} }
else else
{ {
if (subList == shuttleList || subList == shuttleList.ListBox || subList == shuttleList.ListBox.Content) if (subList == ShuttleList || subList == ShuttleList.ListBox || subList == ShuttleList.ListBox.Content)
{ {
subTextBlock.TextColor = new Color(subTextBlock.TextColor, sub.HasTag(SubmarineTag.Shuttle) ? 1.0f : 0.6f); subTextBlock.TextColor = new Color(subTextBlock.TextColor, sub.HasTag(SubmarineTag.Shuttle) ? 1.0f : 0.6f);
} }
@@ -1865,9 +1902,22 @@ namespace Barotrauma
frame.ToolTip = TextManager.Get("ContentPackageMismatch") + "\n\n" + frame.RawToolTip; frame.ToolTip = TextManager.Get("ContentPackageMismatch") + "\n\n" + frame.RawToolTip;
} }
CreateSubmarineClassText(
frame,
sub,
subTextBlock,
subList);
}
private void CreateSubmarineClassText(
GUIComponent parent,
SubmarineInfo sub,
GUITextBlock subTextBlock,
GUIComponent subList)
{
if (sub.HasTag(SubmarineTag.Shuttle)) if (sub.HasTag(SubmarineTag.Shuttle))
{ {
var shuttleText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), frame.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(GUI.IntScale(20), 0) }, var shuttleText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), parent.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(GUI.IntScale(20), 0) },
TextManager.Get("Shuttle", fallBackTag: "RespawnShuttle"), textAlignment: Alignment.CenterRight, font: GUI.SmallFont) TextManager.Get("Shuttle", fallBackTag: "RespawnShuttle"), textAlignment: Alignment.CenterRight, font: GUI.SmallFont)
{ {
TextColor = subTextBlock.TextColor * 0.8f, TextColor = subTextBlock.TextColor * 0.8f,
@@ -1875,10 +1925,10 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
//make shuttles more dim in the sub list (selecting a shuttle as the main sub is allowed but not recommended) //make shuttles more dim in the sub list (selecting a shuttle as the main sub is allowed but not recommended)
if (subList == this.subList.Content) if (subList == this.SubList.Content)
{ {
subTextBlock.TextColor *= 0.8f; subTextBlock.TextColor *= 0.8f;
foreach (GUIComponent child in frame.Children) foreach (GUIComponent child in parent.Children)
{ {
child.Color *= 0.8f; child.Color *= 0.8f;
} }
@@ -1886,17 +1936,17 @@ namespace Barotrauma
} }
else else
{ {
var classText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), frame.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(GUI.IntScale(20), 0) }, var classText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), parent.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(GUI.IntScale(20), 0) },
TextManager.Get($"submarineclass.{sub.SubmarineClass}"), textAlignment: Alignment.CenterRight, font: GUI.SmallFont) TextManager.Get($"submarineclass.{sub.SubmarineClass}"), textAlignment: Alignment.CenterRight, font: GUI.SmallFont)
{ {
UserData = "classtext", UserData = "classtext",
TextColor = subTextBlock.TextColor * 0.8f, TextColor = subTextBlock.TextColor * 0.8f,
ToolTip = subTextBlock.RawToolTip ToolTip = subTextBlock.RawToolTip,
CanBeFocused = false
}; };
} }
} }
public bool VotableClicked(GUIComponent component, object userData) public bool VotableClicked(GUIComponent component, object userData)
{ {
if (GameMain.Client == null) { return false; } if (GameMain.Client == null) { return false; }
@@ -1995,7 +2045,7 @@ namespace Barotrauma
public void AddPlayer(Client client) public void AddPlayer(Client client)
{ {
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), playerList.Content.RectTransform) { MinSize = new Point(0, (int)(30 * GUI.Scale)) }, GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), PlayerList.Content.RectTransform) { MinSize = new Point(0, (int)(30 * GUI.Scale)) },
client.Name, textAlignment: Alignment.CenterLeft, font: GUI.SmallFont, style: null) client.Name, textAlignment: Alignment.CenterLeft, font: GUI.SmallFont, style: null)
{ {
Padding = Vector4.One * 10.0f * GUI.Scale, Padding = Vector4.One * 10.0f * GUI.Scale,
@@ -2109,8 +2159,8 @@ namespace Barotrauma
public void RemovePlayer(Client client) public void RemovePlayer(Client client)
{ {
GUIComponent child = playerList.Content.GetChildByUserData(client); GUIComponent child = PlayerList.Content.GetChildByUserData(client);
if (child != null) { playerList.RemoveChild(child); } if (child != null) { PlayerList.RemoveChild(child); }
} }
public void SelectPlayer(GUITextBlock component, GUITextBlock.ClickableArea area) public void SelectPlayer(GUITextBlock component, GUITextBlock.ClickableArea area)
@@ -2459,7 +2509,7 @@ namespace Barotrauma
private bool ClosePlayerFrame(GUIButton button, object userData) private bool ClosePlayerFrame(GUIButton button, object userData)
{ {
PlayerFrame = null; PlayerFrame = null;
playerList.Deselect(); PlayerList.Deselect();
return true; return true;
} }
@@ -2520,7 +2570,7 @@ namespace Barotrauma
GUI.Style.Apply(micIcon, targetMicStyle); GUI.Style.Apply(micIcon, targetMicStyle);
} }
foreach (GUIComponent child in playerList.Content.Children) foreach (GUIComponent child in PlayerList.Content.Children)
{ {
if (child.UserData is Client client) if (child.UserData is Client client)
{ {
@@ -2752,13 +2802,13 @@ namespace Barotrauma
appearanceFrame.ClearChildren(); appearanceFrame.ClearChildren();
var info = GameMain.Client.CharacterInfo; var info = GameMain.Client.CharacterInfo ?? Character.Controlled?.Info;
CharacterAppearanceCustomizationMenu = new CharacterInfo.AppearanceCustomizationMenu(info, appearanceFrame) CharacterAppearanceCustomizationMenu = new CharacterInfo.AppearanceCustomizationMenu(info, appearanceFrame)
{ {
OnHeadSwitch = menu => OnHeadSwitch = menu =>
{ {
StoreHead(true); StoreHead(true);
UpdateJobPreferences(); UpdateJobPreferences(info);
SelectAppearanceTab(button, _); SelectAppearanceTab(button, _);
}, },
OnSliderMoved = (bar, scroll) => OnSliderMoved = (bar, scroll) =>
@@ -2818,7 +2868,7 @@ namespace Barotrauma
} }
} }
UpdateJobPreferences(); UpdateJobPreferences(GameMain.Client.CharacterInfo ?? Character.Controlled?.Info);
if (moveToNext) if (moveToNext)
{ {
@@ -3014,16 +3064,16 @@ namespace Barotrauma
public void SelectMode(int modeIndex) public void SelectMode(int modeIndex)
{ {
if (modeIndex < 0 || modeIndex >= modeList.Content.CountChildren) { return; } if (modeIndex < 0 || modeIndex >= ModeList.Content.CountChildren) { return; }
if ((GameModePreset)modeList.Content.GetChild(modeIndex).UserData != GameModePreset.MultiPlayerCampaign) if ((GameModePreset)ModeList.Content.GetChild(modeIndex).UserData != GameModePreset.MultiPlayerCampaign)
{ {
ToggleCampaignMode(false); ToggleCampaignMode(false);
} }
var prevMode = modeList.Content.GetChild(selectedModeIndex).UserData as GameModePreset; var prevMode = ModeList.Content.GetChild(selectedModeIndex).UserData as GameModePreset;
if ((HighlightedModeIndex == selectedModeIndex || HighlightedModeIndex < 0) && modeList.SelectedIndex != modeIndex) { modeList.Select(modeIndex, true); } if ((HighlightedModeIndex == selectedModeIndex || HighlightedModeIndex < 0) && ModeList.SelectedIndex != modeIndex) { ModeList.Select(modeIndex, true); }
selectedModeIndex = modeIndex; selectedModeIndex = modeIndex;
if ((prevMode == GameModePreset.PvP) != (SelectedMode == GameModePreset.PvP)) if ((prevMode == GameModePreset.PvP) != (SelectedMode == GameModePreset.PvP))
@@ -3043,7 +3093,7 @@ namespace Barotrauma
public void HighlightMode(int modeIndex) public void HighlightMode(int modeIndex)
{ {
if (modeIndex < 0 || modeIndex >= modeList.Content.CountChildren) { return; } if (modeIndex < 0 || modeIndex >= ModeList.Content.CountChildren) { return; }
HighlightedModeIndex = modeIndex; HighlightedModeIndex = modeIndex;
RefreshGameModeContent(); RefreshGameModeContent();
@@ -3139,7 +3189,7 @@ namespace Barotrauma
RefreshEnabledElements(); RefreshEnabledElements();
if (enabled) if (enabled)
{ {
modeList.Select(GameModePreset.MultiPlayerCampaign, true); ModeList.Select(GameModePreset.MultiPlayerCampaign, true);
} }
} }
@@ -3147,17 +3197,17 @@ namespace Barotrauma
{ {
string name = submarine?.Name; string name = submarine?.Name;
bool displayed = false; bool displayed = false;
subList.OnSelected -= VotableClicked; SubList.OnSelected -= VotableClicked;
subList.Deselect(); SubList.Deselect();
subPreviewContainer.ClearChildren(); subPreviewContainer.ClearChildren();
foreach (GUIComponent child in subList.Content.Children) foreach (GUIComponent child in SubList.Content.Children)
{ {
if (!(child.UserData is SubmarineInfo sub)) { continue; } if (!(child.UserData is SubmarineInfo sub)) { continue; }
//just check the name, even though the campaign sub may not be the exact same version //just check the name, even though the campaign sub may not be the exact same version
//we're selecting the sub just for show, the selection is not actually used for anything //we're selecting the sub just for show, the selection is not actually used for anything
if (sub.Name == name) if (sub.Name == name)
{ {
subList.Select(sub); SubList.Select(sub);
if (SubmarineInfo.SavedSubmarines.Contains(sub)) if (SubmarineInfo.SavedSubmarines.Contains(sub))
{ {
CreateSubPreview(sub); CreateSubPreview(sub);
@@ -3166,7 +3216,7 @@ namespace Barotrauma
break; break;
} }
} }
subList.OnSelected += VotableClicked; SubList.OnSelected += VotableClicked;
if (!displayed) if (!displayed)
{ {
CreateSubPreview(submarine); CreateSubPreview(submarine);
@@ -3194,11 +3244,13 @@ namespace Barotrauma
return true; return true;
} }
private void UpdateJobPreferences() private void UpdateJobPreferences(CharacterInfo characterInfo)
{ {
if (characterInfo == null) { return; }
GUICustomComponent characterIcon = JobPreferenceContainer.GetChild<GUICustomComponent>(); GUICustomComponent characterIcon = JobPreferenceContainer.GetChild<GUICustomComponent>();
JobPreferenceContainer.RemoveChild(characterIcon); JobPreferenceContainer.RemoveChild(characterIcon);
GameMain.Client.CharacterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.4f), JobPreferenceContainer.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.025f) }); characterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.4f), JobPreferenceContainer.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.025f) });
GUIListBox listBox = JobPreferenceContainer.GetChild<GUIListBox>(); GUIListBox listBox = JobPreferenceContainer.GetChild<GUIListBox>();
/*foreach (Sprite sprite in jobPreferenceSprites) { sprite.Remove(); } /*foreach (Sprite sprite in jobPreferenceSprites) { sprite.Remove(); }
@@ -3231,7 +3283,7 @@ namespace Barotrauma
variantButton.OnClicked = (btn, obj) => variantButton.OnClicked = (btn, obj) =>
{ {
btn.Parent.UserData = obj; btn.Parent.UserData = obj;
UpdateJobPreferences(); UpdateJobPreferences(characterInfo);
return false; return false;
}; };
} }
@@ -3340,7 +3392,7 @@ namespace Barotrauma
//matching sub found and already selected, all good //matching sub found and already selected, all good
if (sub != null) if (sub != null)
{ {
if (subList == this.subList) if (subList == this.SubList)
{ {
CreateSubPreview(sub); CreateSubPreview(sub);
} }
@@ -3533,6 +3585,249 @@ namespace Barotrauma
} }
} }
private List<SubmarineInfo> visibilityMenuOrder = new List<SubmarineInfo>();
private void CreateSubmarineVisibilityMenu()
{
var messageBox = new GUIMessageBox(TextManager.Get("SubmarineVisibility"), "",
buttons: Array.Empty<string>(),
relativeSize: new Vector2(0.75f, 0.75f));
messageBox.Content.ChildAnchor = Anchor.TopCenter;
var columns = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), messageBox.Content.RectTransform), isHorizontal: true);
GUILayoutGroup createColumn(float width)
=> new GUILayoutGroup(new RectTransform(new Vector2(width, 1.0f), columns.RectTransform))
{ Stretch = true };
GUIListBox createColumnListBox(string labelTag)
{
var column = createColumn(0.45f);
var label = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), column.RectTransform),
TextManager.Get(labelTag), textAlignment: Alignment.Center);
return new GUIListBox(new RectTransform(new Vector2(1.0f, 0.9f), column.RectTransform))
{
CurrentSelectMode = GUIListBox.SelectMode.RequireShiftToSelectMultiple,
CurrentDragMode = GUIListBox.DragMode.DragOutsideBox,
HideDraggedElement = true
};
}
void handleDraggingAcrossLists(GUIListBox from, GUIListBox to)
{
//TODO: put this in a static class once modding-refactor gets merged
if (to.Rect.Contains(PlayerInput.MousePosition) && from.DraggedElement != null)
{
//move the dragged elements to the index determined previously
var draggedElement = from.DraggedElement;
var selected = from.AllSelected.ToList();
selected.Sort((a, b) => from.Content.GetChildIndex(a) - from.Content.GetChildIndex(b));
float oldCount = to.Content.CountChildren;
float newCount = oldCount + selected.Count;
var offset = draggedElement.RectTransform.AbsoluteOffset;
offset += from.Content.Rect.Location;
offset -= to.Content.Rect.Location;
for (int i = 0; i < selected.Count; i++)
{
var c = selected[i];
c.Parent.RemoveChild(c);
c.RectTransform.Parent = to.Content.RectTransform;
c.RectTransform.RepositionChildInHierarchy((int)oldCount+i);
}
from.DraggedElement = null;
from.Deselect();
from.RecalculateChildren();
from.RectTransform.RecalculateScale(true);
to.RecalculateChildren();
to.RectTransform.RecalculateScale(true);
to.Select(selected);
//recalculate the dragged element's offset so it doesn't jump around
draggedElement.RectTransform.AbsoluteOffset = offset;
to.DraggedElement = draggedElement;
to.BarScroll = to.BarScroll * (oldCount / newCount);
}
}
var visibleSubsList = createColumnListBox("VisibleSubmarines");
var centerColumn = createColumn(0.1f);
void centerSpacing()
{
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.4f), centerColumn.RectTransform), style: null);
}
GUIButton centerButton(string style)
=> new GUIButton(
new RectTransform(new Vector2(1.0f, 0.1f), centerColumn.RectTransform),
style: style);
var hiddenSubsList = createColumnListBox("HiddenSubmarines");
void addSubToList(SubmarineInfo sub, GUIListBox list)
{
var modFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.08f), list.Content.RectTransform),
style: "ListBoxElement")
{
UserData = sub
};
var frameContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), modFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.02f
};
var dragIndicator = new GUIButton(new RectTransform(new Vector2(0.1f, 0.5f), frameContent.RectTransform, scaleBasis: ScaleBasis.BothHeight),
style: "GUIDragIndicator")
{
CanBeFocused = false
};
var subName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), frameContent.RectTransform),
text: sub.Name)
{
CanBeFocused = false
};
CreateSubmarineClassText(
frameContent,
sub,
subName,
list.Content);
}
foreach (var sub in GameMain.Client.ServerSubmarines
.OrderBy(s => visibilityMenuOrder.Contains(s))
.ThenBy(s => visibilityMenuOrder.IndexOf(s)))
{
addSubToList(sub,
GameMain.Client.ServerSettings.HiddenSubs.Contains(sub.Name) ? hiddenSubsList : visibleSubsList);
}
void onRearranged(GUIListBox listBox, object userData)
{
visibilityMenuOrder.Clear();
visibilityMenuOrder.AddRange(visibleSubsList.Content.Children.Select(c => c.UserData as SubmarineInfo));
visibilityMenuOrder.AddRange(hiddenSubsList.Content.Children.Select(c => c.UserData as SubmarineInfo));
}
visibleSubsList.OnRearranged = onRearranged;
hiddenSubsList.OnRearranged = onRearranged;
void swapListItems(GUIListBox from, GUIListBox to)
{
to.Deselect();
var selected = from.AllSelected.ToArray();
int lastIndex = from.Content.GetChildIndex(selected.LastOrDefault());
int nextIndex = lastIndex + 1;
GUIComponent nextComponent = null;
if (lastIndex >= 0 && nextIndex < from.Content.CountChildren)
{
nextComponent = from.Content.GetChild(nextIndex);
}
foreach (var frame in selected)
{
frame.Parent.RemoveChild(frame);
frame.RectTransform.Parent = to.Content.RectTransform;
}
from.RecalculateChildren();
from.RectTransform.RecalculateScale(true);
to.RecalculateChildren();
to.RectTransform.RecalculateScale(true);
to.Select(selected);
if (nextComponent != null) { from.Select(nextComponent.ToEnumerable()); }
}
centerSpacing();
var visibleToHidden = centerButton("GUIButtonToggleRight");
visibleToHidden.OnClicked = (button, o) =>
{
swapListItems(visibleSubsList, hiddenSubsList);
return false;
};
var hiddenToVisible = centerButton("GUIButtonToggleLeft");
hiddenToVisible.OnClicked = (button, o) =>
{
swapListItems(hiddenSubsList, visibleSubsList);
return false;
};
centerSpacing();
var buttonLayout
= new GUILayoutGroup(new RectTransform(new Vector2(0.7f, 0.1f), messageBox.Content.RectTransform),
isHorizontal: true)
{
RelativeSpacing = 0.01f
};
var cancelButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonLayout.RectTransform),
TextManager.Get("Cancel"))
{
OnClicked = (button, o) =>
{
messageBox.Close();
return false;
}
};
var okButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonLayout.RectTransform),
TextManager.Get("OK"))
{
OnClicked = (button, o) =>
{
var hiddenSubs = GameMain.Client.ServerSettings.HiddenSubs;
hiddenSubs.Clear();
hiddenSubs.UnionWith(hiddenSubsList.Content.Children.Select(c => (c.UserData as SubmarineInfo).Name));
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.HiddenSubs);
messageBox.Close();
return false;
}
};
new GUICustomComponent(new RectTransform(Vector2.Zero, messageBox.RectTransform),
onUpdate: (f, component) =>
{
handleDraggingAcrossLists(visibleSubsList, hiddenSubsList);
handleDraggingAcrossLists(hiddenSubsList, visibleSubsList);
if (PlayerInput.PrimaryMouseButtonClicked()
&& !GUI.IsMouseOn(visibleToHidden)
&& !GUI.IsMouseOn(hiddenToVisible))
{
if (!GUI.IsMouseOn(hiddenSubsList)
|| !hiddenSubsList.Content.IsParentOf(GUI.MouseOn))
{
hiddenSubsList.Deselect();
}
if (!GUI.IsMouseOn(visibleSubsList)
|| !visibleSubsList.Content.IsParentOf(GUI.MouseOn))
{
visibleSubsList.Deselect();
}
}
},
onDraw: (spriteBatch, component) =>
{
visibleSubsList.DraggedElement?.DrawManually(spriteBatch, true, true);
hiddenSubsList.DraggedElement?.DrawManually(spriteBatch, true, true);
});
}
public void UpdateSubVisibility()
{
foreach (GUIComponent child in SubList.Content.Children)
{
if (!(child.UserData is SubmarineInfo sub)) { continue; }
child.Visible = !GameMain.Client.ServerSettings.HiddenSubs.Contains(sub.Name)
&& (string.IsNullOrEmpty(subSearchBox.Text) || sub.DisplayName.Contains(subSearchBox.Text, StringComparison.OrdinalIgnoreCase));
}
}
public void OnRoundEnded() public void OnRoundEnded()
{ {
CampaignCharacterDiscarded = false; CampaignCharacterDiscarded = false;
@@ -43,7 +43,7 @@ namespace Barotrauma
CoroutineManager.StartCoroutine(UpdateColorFade(from, to, duration)); CoroutineManager.StartCoroutine(UpdateColorFade(from, to, duration));
} }
private IEnumerable<object> UpdateColorFade(Color from, Color to, float duration) private IEnumerable<CoroutineStatus> UpdateColorFade(Color from, Color to, float duration)
{ {
while (Selected != this) while (Selected != this)
{ {
@@ -578,6 +578,7 @@ namespace Barotrauma
RecalculateHolder(); RecalculateHolder();
} }
serverInfo.CreatePreviewWindow(serverPreview.Content); serverInfo.CreatePreviewWindow(serverPreview.Content);
serverPreview.ForceLayoutRecalculation();
btn.Children.ForEach(c => c.SpriteEffects = serverPreviewContainer.Visible ? SpriteEffects.None : SpriteEffects.FlipHorizontally); btn.Children.ForEach(c => c.SpriteEffects = serverPreviewContainer.Visible ? SpriteEffects.None : SpriteEffects.FlipHorizontally);
} }
return true; return true;
@@ -1715,7 +1716,7 @@ namespace Barotrauma
CoroutineManager.StartCoroutine(WaitForRefresh()); CoroutineManager.StartCoroutine(WaitForRefresh());
} }
private IEnumerable<object> WaitForRefresh() private IEnumerable<CoroutineStatus> WaitForRefresh()
{ {
waitingForRefresh = true; waitingForRefresh = true;
if (refreshDisableTimer > DateTime.Now) if (refreshDisableTimer > DateTime.Now)
@@ -2058,7 +2059,7 @@ namespace Barotrauma
FilterServers(); FilterServers();
} }
private IEnumerable<object> EstimateLobbyPing(ServerInfo serverInfo, GUITextBlock serverPingText) private IEnumerable<CoroutineStatus> EstimateLobbyPing(ServerInfo serverInfo, GUITextBlock serverPingText)
{ {
while (!steamPingInfoReady) while (!steamPingInfoReady)
{ {
@@ -2096,7 +2097,7 @@ namespace Barotrauma
waitingForRefresh = false; waitingForRefresh = false;
} }
private IEnumerable<object> SendMasterServerRequest() private IEnumerable<CoroutineStatus> SendMasterServerRequest()
{ {
RestClient client = null; RestClient client = null;
try try
@@ -2271,7 +2272,7 @@ namespace Barotrauma
return true; return true;
} }
private IEnumerable<object> ConnectToServer(string endpoint, string serverName) private IEnumerable<CoroutineStatus> ConnectToServer(string endpoint, string serverName)
{ {
string serverIP = null; string serverIP = null;
UInt64 serverSteamID = SteamManager.SteamIDStringToUInt64(endpoint); UInt64 serverSteamID = SteamManager.SteamIDStringToUInt64(endpoint);
@@ -304,7 +304,7 @@ namespace Barotrauma
float subscribePollAdditionalWait = 0.0f; float subscribePollAdditionalWait = 0.0f;
private IEnumerable<object> PollSubscribedItems() private IEnumerable<CoroutineStatus> PollSubscribedItems()
{ {
if (!SteamManager.IsInitialized) { yield return CoroutineStatus.Success; } if (!SteamManager.IsInitialized) { yield return CoroutineStatus.Success; }
@@ -364,7 +364,7 @@ namespace Barotrauma
} }
} }
public IEnumerable<object> RefreshDownloadState() public IEnumerable<CoroutineStatus> RefreshDownloadState()
{ {
bool isDownloading = true; bool isDownloading = true;
while (true) while (true)
@@ -831,7 +831,7 @@ namespace Barotrauma
} }
} }
private IEnumerable<object> WaitForItemPreviewDownloaded(Steamworks.Ugc.Item? item, GUIListBox listBox, string previewImagePath) private IEnumerable<CoroutineStatus> WaitForItemPreviewDownloaded(Steamworks.Ugc.Item? item, GUIListBox listBox, string previewImagePath)
{ {
while (true) while (true)
{ {
@@ -1835,7 +1835,7 @@ namespace Barotrauma
} }
private IEnumerable<object> WaitForPublish(SteamManager.WorkshopPublishStatus workshopPublishStatus) private IEnumerable<CoroutineStatus> WaitForPublish(SteamManager.WorkshopPublishStatus workshopPublishStatus)
{ {
var item = workshopPublishStatus.Item; var item = workshopPublishStatus.Item;
var coroutine = workshopPublishStatus.Coroutine; var coroutine = workshopPublishStatus.Coroutine;
@@ -369,11 +369,20 @@ namespace Barotrauma
{ {
ToolTip = TextManager.Get("AddSubToolTip") ToolTip = TextManager.Get("AddSubToolTip")
}; };
List<(string Name, SubmarineInfo Sub)> subs = new List<(string Name, SubmarineInfo Sub)>();
foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines) foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{ {
if (sub.Type != SubmarineType.Player) { continue; } if (sub.Type != SubmarineType.Player) { continue; }
linkedSubBox.AddItem(sub.Name, sub); subs.Add((sub.Name, sub));
} }
foreach (var (name, sub) in subs.OrderBy(tuple => tuple.Name))
{
linkedSubBox.AddItem(name, sub);
}
linkedSubBox.OnSelected += SelectLinkedSub; linkedSubBox.OnSelected += SelectLinkedSub;
linkedSubBox.OnDropped += (component, obj) => linkedSubBox.OnDropped += (component, obj) =>
{ {
@@ -1220,11 +1229,19 @@ namespace Barotrauma
string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder); string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
linkedSubBox.ClearChildren(); linkedSubBox.ClearChildren();
List<(string Name, SubmarineInfo Sub)> subs = new List<(string Name, SubmarineInfo Sub)>();
foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines) foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{ {
if (sub.Type != SubmarineType.Player) { continue; } if (sub.Type != SubmarineType.Player) { continue; }
if (Path.GetDirectoryName(Path.GetFullPath(sub.FilePath)) == downloadFolder) { continue; } if (Path.GetDirectoryName(Path.GetFullPath(sub.FilePath)) == downloadFolder) { continue; }
linkedSubBox.AddItem(sub.Name, sub); subs.Add((sub.Name, sub));
}
foreach (var (subName, sub) in subs.OrderBy(tuple => tuple.Name))
{
linkedSubBox.AddItem(subName, sub);
} }
cam.UpdateTransform(); cam.UpdateTransform();
@@ -1294,7 +1311,7 @@ namespace Barotrauma
/// </summary> /// </summary>
/// <see cref="AutoSave"/> /// <see cref="AutoSave"/>
/// <returns></returns> /// <returns></returns>
private static IEnumerable<object> AutoSaveCoroutine() private static IEnumerable<CoroutineStatus> AutoSaveCoroutine()
{ {
DateTime target = DateTime.Now.AddMinutes(GameSettings.AutoSaveIntervalSeconds); DateTime target = DateTime.Now.AddMinutes(GameSettings.AutoSaveIntervalSeconds);
DateTime tempTarget = DateTime.Now; DateTime tempTarget = DateTime.Now;
@@ -1998,14 +2015,21 @@ namespace Barotrauma
var gapPositionDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), gapPositionGroup.RectTransform), var gapPositionDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), gapPositionGroup.RectTransform),
text: "", selectMultiple: true); text: "", selectMultiple: true);
Submarine.MainSub.Info?.OutpostModuleInfo?.DetermineGapPositions(Submarine.MainSub); var outpostModuleInfo = Submarine.MainSub.Info?.OutpostModuleInfo;
foreach (var gapPos in Enum.GetValues(typeof(OutpostModuleInfo.GapPosition))) if (outpostModuleInfo != null)
{ {
if ((OutpostModuleInfo.GapPosition)gapPos == OutpostModuleInfo.GapPosition.None) { continue; } if (outpostModuleInfo.GapPositions == OutpostModuleInfo.GapPosition.None)
gapPositionDropDown.AddItem(TextManager.Capitalize(gapPos.ToString()), gapPos);
if (Submarine.MainSub.Info?.OutpostModuleInfo?.GapPositions.HasFlag((OutpostModuleInfo.GapPosition)gapPos) ?? false)
{ {
gapPositionDropDown.SelectItem(gapPos); outpostModuleInfo.DetermineGapPositions(Submarine.MainSub);
}
foreach (var gapPos in Enum.GetValues(typeof(OutpostModuleInfo.GapPosition)))
{
if ((OutpostModuleInfo.GapPosition)gapPos == OutpostModuleInfo.GapPosition.None) { continue; }
gapPositionDropDown.AddItem(TextManager.Capitalize(gapPos.ToString()), gapPos);
if (outpostModuleInfo.GapPositions.HasFlag((OutpostModuleInfo.GapPosition)gapPos))
{
gapPositionDropDown.SelectItem(gapPos);
}
} }
} }
@@ -4774,7 +4798,7 @@ namespace Barotrauma
if (dummyCharacter != null) if (dummyCharacter != null)
{ {
dummyCharacter.AnimController.FindHull(dummyCharacter.CursorWorldPosition, false); dummyCharacter.AnimController.FindHull(dummyCharacter.CursorWorldPosition, setSubmarine: false);
foreach (Item item in dummyCharacter.Inventory.AllItems) foreach (Item item in dummyCharacter.Inventory.AllItems)
{ {
@@ -221,7 +221,7 @@ namespace Barotrauma.Sounds
{ {
if (!MathUtils.IsValid(value)) { return; } if (!MathUtils.IsValid(value)) { return; }
gain = Math.Clamp(value, 0.0f, 1.0f); gain = Math.Max(value, 0.0f);
if (ALSourceIndex < 0) { return; } if (ALSourceIndex < 0) { return; }
@@ -525,6 +525,8 @@ namespace Barotrauma.Sounds
throw new Exception("Failed to bind buffer to source (" + ALSourceIndex.ToString() + ":" + sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex) + "," + alBuffer.ToString() + "): " + debugName + ", " + Al.GetErrorString(alError)); throw new Exception("Failed to bind buffer to source (" + ALSourceIndex.ToString() + ":" + sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex) + "," + alBuffer.ToString() + "): " + debugName + ", " + Al.GetErrorString(alError));
} }
SetProperties();
Al.SourcePlay(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex)); Al.SourcePlay(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex));
alError = Al.GetError(); alError = Al.GetError();
if (alError != Al.NoError) if (alError != Al.NoError)
@@ -570,16 +572,9 @@ namespace Barotrauma.Sounds
} }
} }
Sound.Owner.InitStreamThread(); Sound.Owner.InitStreamThread();
SetProperties();
} }
} }
this.Position = position;
this.Gain = gain;
this.FrequencyMultiplier = freqMult;
this.Looping = false;
this.Near = near;
this.Far = far;
this.Category = category;
#if !DEBUG #if !DEBUG
} }
catch catch
@@ -594,6 +589,17 @@ namespace Barotrauma.Sounds
} }
#endif #endif
void SetProperties()
{
this.Position = position;
this.Gain = gain;
this.FrequencyMultiplier = freqMult;
this.Looping = false;
this.Near = near;
this.Far = far;
this.Category = category;
}
Sound.Owner.Update(); Sound.Owner.Update();
} }
@@ -147,7 +147,7 @@ namespace Barotrauma
MathUtils.NearlyEqual(rangeA, rangeB); MathUtils.NearlyEqual(rangeA, rangeB);
} }
public static IEnumerable<object> Init() public static IEnumerable<CoroutineStatus> Init()
{ {
OverrideMusicType = null; OverrideMusicType = null;
@@ -826,7 +826,7 @@ namespace Barotrauma
//find appropriate music for the current situation //find appropriate music for the current situation
string currentMusicType = GetCurrentMusicType(); string currentMusicType = GetCurrentMusicType();
float currentIntensity = GameMain.GameSession?.EventManager != null ? float currentIntensity = GameMain.GameSession?.EventManager != null ?
GameMain.GameSession.EventManager.CurrentIntensity * 100.0f : 0.0f; GameMain.GameSession.EventManager.MusicIntensity * 100.0f : 0.0f;
IEnumerable<BackgroundMusic> suitableMusic = GetSuitableMusicClips(currentMusicType, currentIntensity); IEnumerable<BackgroundMusic> suitableMusic = GetSuitableMusicClips(currentMusicType, currentIntensity);
int mainTrackIndex = 0; int mainTrackIndex = 0;
@@ -84,7 +84,7 @@ namespace Barotrauma
Vector4 sourceVector = Vector4.Zero; Vector4 sourceVector = Vector4.Zero;
bool temp2 = false; bool temp2 = false;
int maxLoadRetries = 3; int maxLoadRetries = File.Exists(FilePath) ? 3 : 0;
for (int i = 0; i <= maxLoadRetries; i++) for (int i = 0; i <= maxLoadRetries; i++)
{ {
try try
@@ -169,7 +169,8 @@ namespace Barotrauma
} }
else else
{ {
DebugConsole.ThrowError($"Sprite \"{file}\" not found! {Environment.StackTrace.CleanupStackTrace()}"); DebugConsole.ThrowError($"Sprite \"{file}\" not found!");
DebugConsole.Log(Environment.StackTrace.CleanupStackTrace());
} }
return null; return null;
@@ -434,130 +434,8 @@ namespace Barotrauma
return Color.Lerp(gradient[(int)scaledT], gradient[(int)Math.Min(scaledT + 1, gradient.Length - 1)], (scaledT - (int)scaledT)); return Color.Lerp(gradient[(int)scaledT], gradient[(int)Math.Min(scaledT + 1, gradient.Length - 1)], (scaledT - (int)scaledT));
} }
public static string WrapText(string text, float lineLength, ScalableFont font, float textScale = 1.0f, bool playerInput = false) //TODO: could integrate this into the ScalableFont class directly public static string WrapText(string text, float lineLength, ScalableFont font, float textScale = 1.0f)
{ => font.WrapText(text, lineLength / textScale);
Vector2 textSize = font.MeasureString(text);
if (textSize.X <= lineLength) { return text; }
if (!playerInput)
{
text = text.Replace("\n", " \n ");
}
List<string> words = new List<string>();
string currWord = "";
for (int i = 0; i < text.Length; i++)
{
if (TextManager.IsCJK(text[i].ToString()))
{
if (currWord.Length > 0)
{
words.Add(currWord);
currWord = "";
}
words.Add(text[i].ToString());
}
else if (text[i] == ' ')
{
if (currWord.Length > 0)
{
words.Add(currWord);
currWord = "";
}
words.Add(string.Empty);
}
else
{
currWord += text[i];
}
}
if (currWord.Length > 0)
{
words.Add(currWord);
currWord = "";
}
StringBuilder wrappedText = new StringBuilder();
float linePos = 0f;
Vector2 spaceSize = font.MeasureString(" ") * textScale;
for (int i = 0; i < words.Count; ++i)
{
string currentWord = words[i];
if (currentWord.Length == 0)
{
// space
currentWord = " ";
}
else if (string.IsNullOrWhiteSpace(currentWord) && currentWord != "\n")
{
continue;
}
Vector2 size = words[i].Length == 0 ? spaceSize : font.MeasureString(currentWord) * textScale;
if (size.X > lineLength)
{
float splitSize = 0.0f;
List<string> splitWord = new List<string>() { string.Empty };
int k = 0;
for (int j = 0; j < currentWord.Length; j++)
{
splitWord[k] += currentWord[j];
splitSize += (font.MeasureString(currentWord[j].ToString()) * textScale).X;
if (splitSize + linePos > lineLength)
{
linePos = splitSize = 0.0f;
splitWord[k] = splitWord[k].Remove(splitWord[k].Length - 1) + "\n";
if (splitWord[k].Length <= 1) { break; }
j--;
splitWord.Add(string.Empty);
k++;
}
}
for (int j = 0; j < splitWord.Count; j++)
{
wrappedText.Append(splitWord[j]);
}
linePos = splitSize;
}
else
{
if (linePos + size.X < lineLength)
{
wrappedText.Append(currentWord);
if (currentWord == "\n")
{
linePos = 0.0f;
}
else
{
linePos += size.X;
}
}
else
{
wrappedText.Append("\n");
wrappedText.Append(currentWord);
linePos = size.X;
}
}
}
if (!playerInput)
{
return wrappedText.ToString().Replace(" \n ", "\n");
}
else
{
return wrappedText.ToString();
}
}
public static void ParseConnectCommand(string[] args, out string name, out string endpoint, out UInt64 lobbyId) public static void ParseConnectCommand(string[] args, out string name, out string endpoint, out UInt64 lobbyId)
{ {
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.15.13.0</Version> <Version>0.15.15.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -122,7 +122,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="NVorbis" Version="0.8.6" /> <PackageReference Include="NVorbis" Version="0.8.6" />
<PackageReference Include="RestSharp" Version="106.6.10" /> <PackageReference Include="RestSharp" Version="106.12.0" />
</ItemGroup> </ItemGroup>
<!-- Sourced from https://stackoverflow.com/a/45248069 --> <!-- Sourced from https://stackoverflow.com/a/45248069 -->
+2 -2
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.15.13.0</Version> <Version>0.15.15.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -123,7 +123,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="NVorbis" Version="0.8.6" /> <PackageReference Include="NVorbis" Version="0.8.6" />
<PackageReference Include="RestSharp" Version="106.6.10" /> <PackageReference Include="RestSharp" Version="106.12.0" />
</ItemGroup> </ItemGroup>
<!-- Sourced from https://stackoverflow.com/a/45248069 --> <!-- Sourced from https://stackoverflow.com/a/45248069 -->
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.15.13.0</Version> <Version>0.15.15.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -126,7 +126,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="NVorbis" Version="0.8.6" /> <PackageReference Include="NVorbis" Version="0.8.6" />
<PackageReference Include="RestSharp" Version="106.6.10" /> <PackageReference Include="RestSharp" Version="106.12.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.15.13.0</Version> <Version>0.15.15.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.15.13.0</Version> <Version>0.15.15.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -60,5 +60,10 @@ namespace Barotrauma
{ {
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateMoney }); GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateMoney });
} }
partial void OnTalentGiven(string talentIdentifier)
{
GameServer.Log($"{GameServer.CharacterLogName(this)} has gained the talent '{talentIdentifier}'", ServerLog.MessageType.Talent);
}
} }
} }
@@ -29,6 +29,7 @@ namespace Barotrauma
if (Character == null || Character.Removed) { return; } if (Character == null || Character.Removed) { return; }
if (prevAmount != newAmount) if (prevAmount != newAmount)
{ {
GameServer.Log($"{GameServer.CharacterLogName(Character)} has gained {newAmount - prevAmount} experience ({prevAmount} -> {newAmount})", ServerLog.MessageType.Talent);
GameMain.NetworkMember.CreateEntityEvent(Character, new object[] { NetEntityEvent.Type.UpdateExperience }); GameMain.NetworkMember.CreateEntityEvent(Character, new object[] { NetEntityEvent.Type.UpdateExperience });
} }
} }
@@ -98,7 +98,7 @@ namespace Barotrauma
{ {
ColoredText msg = queuedMessages.Dequeue(); ColoredText msg = queuedMessages.Dequeue();
Messages.Add(msg); Messages.Add(msg);
if (GameSettings.SaveDebugConsoleLogs) if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging)
{ {
unsavedMessages.Add(msg); unsavedMessages.Add(msg);
if (unsavedMessages.Count >= messagesPerFile) if (unsavedMessages.Count >= messagesPerFile)
@@ -281,7 +281,7 @@ namespace Barotrauma
{ {
var msg = queuedMessages.Dequeue(); var msg = queuedMessages.Dequeue();
Messages.Add(msg); Messages.Add(msg);
if (GameSettings.SaveDebugConsoleLogs) if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging)
{ {
unsavedMessages.Add(msg); unsavedMessages.Add(msg);
if (unsavedMessages.Count >= messagesPerFile) if (unsavedMessages.Count >= messagesPerFile)
@@ -1315,7 +1315,7 @@ namespace Barotrauma
commands.Add(new Command("sub|submarine", "submarine [name]: Select the submarine for the next round.", (string[] args) => commands.Add(new Command("sub|submarine", "submarine [name]: Select the submarine for the next round.", (string[] args) =>
{ {
SubmarineInfo sub = GameMain.NetLobbyScreen.GetSubList().Find(s => s.Name.ToLower() == string.Join(" ", args).ToLower()); SubmarineInfo sub = GameMain.NetLobbyScreen.GetSubList().Find(s => s.Name.Equals(string.Join(" ", args), StringComparison.OrdinalIgnoreCase));
if (sub != null) if (sub != null)
{ {
@@ -1377,7 +1377,7 @@ namespace Barotrauma
commands.Add(new Command("endgame|endround|end", "end/endgame/endround: End the current round.", (string[] args) => commands.Add(new Command("endgame|endround|end", "end/endgame/endround: End the current round.", (string[] args) =>
{ {
if (Screen.Selected == GameMain.NetLobbyScreen) return; if (Screen.Selected == GameMain.NetLobbyScreen) { return; }
GameMain.Server.EndGame(); GameMain.Server.EndGame();
})); }));
@@ -1399,11 +1399,18 @@ namespace Barotrauma
commands.Add(new Command("eventdata", "", (string[] args) => commands.Add(new Command("eventdata", "", (string[] args) =>
{ {
if (args.Length == 0) return; if (args.Length == 0) { return; }
ServerEntityEvent ev = GameMain.Server.EntityEventManager.Events[Convert.ToUInt16(args[0])]; if (!UInt16.TryParse(args[0], NumberStyles.Any, CultureInfo.InvariantCulture, out ushort eventId)) { return; }
ServerEntityEvent ev = GameMain.Server.EntityEventManager.Events.Find(ev => ev.ID == eventId);
if (ev != null) if (ev != null)
{ {
NewMessage(ev.StackTrace.CleanupStackTrace(), Color.Lime); string entityData = "";
if (ev.Entity is { ID: var entityId, Removed: var removed, IdFreed: var idFreed })
{
entityData = $"Entity ID: {entityId}; Entity removed: {removed}; Entity ID freed: {idFreed}";
}
NewMessage($"EventData {eventId}\n{entityData}", Color.Lime);
//NewMessage(ev.StackTrace.CleanupStackTrace(), Color.Lime);
} }
})); }));
@@ -1578,16 +1585,13 @@ namespace Barotrauma
(Client client, Vector2 cursorWorldPos, string[] args) => (Client client, Vector2 cursorWorldPos, string[] args) =>
{ {
Character tpCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args, false); Character tpCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args, false);
if (tpCharacter == null) return; if (tpCharacter != null)
//var cam = GameMain.GameScreen.Cam;
tpCharacter.AnimController.CurrentHull = null;
tpCharacter.Submarine = null;
tpCharacter.AnimController.SetPosition(ConvertUnits.ToSimUnits(cursorWorldPos));
tpCharacter.AnimController.FindHull(cursorWorldPos, true);
if (tpCharacter.AIController?.SteeringManager is IndoorsSteeringManager pathSteering)
{ {
pathSteering.ResetPath(); tpCharacter.TeleportTo(cursorWorldPos);
if (tpCharacter.AIController?.SteeringManager is IndoorsSteeringManager pathSteering)
{
pathSteering.ResetPath();
}
} }
} }
); );
@@ -1779,7 +1783,7 @@ namespace Barotrauma
List<TalentTree> talentTrees = new List<TalentTree>(); List<TalentTree> talentTrees = new List<TalentTree>();
if (args.Length == 0 || args[0].Equals("all", StringComparison.OrdinalIgnoreCase)) if (args.Length == 0 || args[0].Equals("all", StringComparison.OrdinalIgnoreCase))
{ {
talentTrees.AddRange(TalentTree.JobTalentTrees.Values); talentTrees.AddRange(TalentTree.JobTalentTrees);
} }
else else
{ {
@@ -2370,6 +2374,16 @@ namespace Barotrauma
GameMain.Server.CreateEntityEvent(wall); GameMain.Server.CreateEntityEvent(wall);
} }
})); }));
commands.Add(new Command("stallfiletransfers", "stallfiletransfers [seconds]: A debug command that stalls each file transfer packet by the specified duration.", (string[] args) =>
{
float seconds = 0.0f;
if (args.Length > 0)
{
float.TryParse(args[0], out seconds);
}
GameMain.Server.FileSender.StallPacketsTime = seconds;
NewMessage("Set file transfer stall time to " + seconds);
}));
#endif #endif
} }

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