v0.10.5.1

This commit is contained in:
Juan Pablo Arce
2020-09-22 11:31:56 -03:00
parent 44032d0ae0
commit 0002ad2c50
343 changed files with 12276 additions and 5023 deletions

View File

@@ -7,11 +7,18 @@ assignees: ''
---
**QA**
- [ ] Play through tutorials and verify that they are working.
- [ ] Do a smoketest on all game modes
- [ ] Smoketest server hosting (dedicated and client)
- [ ] Install Trick or Trauma and check you can start a round with no obvious issues/errors (to make sure we didn't unintentionally break compatibility with older mods).
- [ ] Play through one single player campaign round.
- [ ] Do a smoketest in a language other than English.
**Code:**
- [ ] Build and upload dedicated server
- [ ] Verify that Vanilla content package hashes match between Windows/Mac/Linux
- [ ] Run "checkmissingloca" command to make sure localization files are up-to-date.
- [ ] Install Trick or Trauma and check you can start a round with no obvious issues/errors (to make sure we didn't unintentionally break compatibility with older mods).
- [ ] Prepare new main menu content (changelog)
- [ ] Prepare public github repo for pushing the new changes

View File

@@ -261,7 +261,7 @@ namespace Barotrauma
if (targetPos == Vector2.Zero)
{
Vector2 moveInput = Vector2.Zero;
if (allowMove)
if (allowMove && !Freeze)
{
if (GUI.KeyboardDispatcher.Subscriber == null)
{

View File

@@ -1,6 +1,5 @@
using Microsoft.Xna.Framework;
using FarseerPhysics;
using System.Linq;
namespace Barotrauma
{
@@ -64,6 +63,27 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60 + offset), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
}
for (int i = 0; i < ObjectiveManager.Objectives.Count; i++)
{
var objective = ObjectiveManager.Objectives[i];
int offsetMultiplier;
if (ObjectiveManager.CurrentOrder == null)
{
if (i == 0)
{
continue;
}
else
{
offsetMultiplier = i - 1;
}
}
else
{
offsetMultiplier = i + 1;
}
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(120, offsetMultiplier * 18 + 100), $"{objective.DebugTag} ({objective.Priority.FormatZeroDecimal()})", Color.White, Color.Black * 0.5f);
}
}
if (steeringManager is IndoorsSteeringManager pathSteering)

View File

@@ -409,11 +409,6 @@ namespace Barotrauma
emitter.Emit(1.0f, limb.WorldPosition, character.CurrentHull, amountMultiplier: gibParticleAmount);
}
if (!string.IsNullOrEmpty(character.BloodDecalName))
{
character.CurrentHull?.AddDecal(character.BloodDecalName, limb.WorldPosition, MathHelper.Clamp(limb.Mass, 0.5f, 2.0f));
}
}
if (playSound)

View File

@@ -97,8 +97,6 @@ namespace Barotrauma
set { chromaticAberrationStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public string BloodDecalName => Params.BloodDecal;
private readonly List<ParticleEmitter> bloodEmitters = new List<ParticleEmitter>();
public IEnumerable<ParticleEmitter> BloodEmitters
{
@@ -617,7 +615,7 @@ namespace Barotrauma
partial void SetOrderProjSpecific(Order order, string orderOption)
{
GameMain.GameSession?.CrewManager?.DisplayCharacterOrder(this, order, orderOption);
GameMain.GameSession?.CrewManager?.AddCurrentOrderIcon(this, order, orderOption);
}
public static void AddAllToGUIUpdateList()
@@ -786,7 +784,7 @@ namespace Barotrauma
}
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
{
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionIcon." + CampaignInteractionType);
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
if (iconStyle != null)
{
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.WorldPosition ?? WorldPosition + Vector2.UnitY * 100.0f;
@@ -825,15 +823,19 @@ namespace Barotrauma
/// Creates a progress bar that's "linked" to the specified object (or updates an existing one if there's one already linked to the object)
/// The progress bar will automatically fade out after 1 sec if the method hasn't been called during that time
/// </summary>
public HUDProgressBar UpdateHUDProgressBar(object linkedObject, Vector2 worldPosition, float progress, Color emptyColor, Color fullColor)
public HUDProgressBar UpdateHUDProgressBar(object linkedObject, Vector2 worldPosition, float progress, Color emptyColor, Color fullColor, string textTag = "")
{
if (controlled != this) return null;
if (controlled != this) { return null; }
if (!hudProgressBars.TryGetValue(linkedObject, out HUDProgressBar progressBar))
{
progressBar = new HUDProgressBar(worldPosition, Submarine, emptyColor, fullColor);
progressBar = new HUDProgressBar(worldPosition, Submarine, emptyColor, fullColor, textTag);
hudProgressBars.Add(linkedObject, progressBar);
}
else
{
progressBar.TextTag = textTag;
}
progressBar.WorldPosition = worldPosition;
progressBar.FadeTimer = Math.Max(progressBar.FadeTimer, 1.0f);
@@ -859,7 +861,7 @@ namespace Barotrauma
}
var selectedSound = matchingSounds.GetRandom();
if (selectedSound?.Sound == null) { return; }
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, AnimController.WorldPosition, selectedSound.Volume, selectedSound.Range, CurrentHull);
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, AnimController.WorldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: CurrentHull);
soundTimer = soundInterval;
}

View File

@@ -52,7 +52,6 @@ namespace Barotrauma
return
character?.Inventory != null &&
character.AllowInput &&
!character.LockHands &&
(controller?.User != character || !controller.HideHUD) &&
!IsCampaignInterfaceOpen &&
!ConversationAction.FadeScreenToBlack;
@@ -227,7 +226,7 @@ namespace Barotrauma
Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
}
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen)
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() == null)))
{
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
{
@@ -307,6 +306,14 @@ namespace Barotrauma
{
progressBar.Draw(spriteBatch, cam);
}
foreach (Character npc in Character.CharacterList)
{
if (npc.CampaignInteractionType == CampaignMode.InteractionType.None || npc.Submarine != character.Submarine || npc.IsDead || npc.IsIncapacitated) { continue; }
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionIcon." + npc.CampaignInteractionType);
GUI.DrawIndicator(spriteBatch, npc.WorldPosition, cam, 500.0f, iconStyle.GetDefaultSprite(), iconStyle.Color);
}
}
if (character.SelectedConstruction != null &&
@@ -355,7 +362,7 @@ namespace Barotrauma
}
if (ShouldDrawInventory(character))
{
character.Inventory.Locked = LockInventory(character);
character.Inventory.Locked = character == Character.Controlled && LockInventory(character);
character.Inventory.DrawOwn(spriteBatch);
character.Inventory.CurrentLayout = CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null ?
CharacterInventory.Layout.Default :
@@ -369,14 +376,10 @@ namespace Barotrauma
{
if (character.SelectedCharacter.CanInventoryBeAccessed)
{
///character.Inventory.CurrentLayout = Alignment.Left;
character.SelectedCharacter.Inventory.Locked = false;
character.SelectedCharacter.Inventory.CurrentLayout = CharacterInventory.Layout.Left;
character.SelectedCharacter.Inventory.DrawOwn(spriteBatch);
}
else
{
//character.Inventory.CurrentLayout = (CharacterHealth.OpenHealthWindow == null) ? Alignment.Center : Alignment.Left;
}
if (CharacterHealth.OpenHealthWindow == character.SelectedCharacter.CharacterHealth)
{
character.SelectedCharacter.CharacterHealth.Alignment = Alignment.Left;
@@ -450,7 +453,6 @@ namespace Barotrauma
private static bool LockInventory(Character character)
{
if (character?.Inventory == null || !character.AllowInput || character.LockHands || IsCampaignInterfaceOpen) { return true; }
return character.ShouldLockHud();
}

View File

@@ -14,7 +14,8 @@ namespace Barotrauma
{
if (this != Controlled)
{
if (GameMain.Client.EndCinematic != null) // Freezes the characters during the ending cinematic
if (GameMain.Client.EndCinematic != null &&
GameMain.Client.EndCinematic.Running) // Freezes the characters during the ending cinematic
{
AnimController.Frozen = true;
memState.Clear();

View File

@@ -38,25 +38,43 @@ namespace Barotrauma
public Vector2 Size;
private Submarine parentSub;
private readonly Submarine parentSub;
public string Text
{
get;
private set;
}
public HUDProgressBar(Vector2 worldPosition, Submarine parentSubmarine = null)
: this(worldPosition, parentSubmarine, GUI.Style.Red, GUI.Style.Green)
private string textTag;
public string TextTag
{
get { return textTag; }
set
{
if (textTag == value) { return; }
textTag = value;
Text = string.IsNullOrEmpty(textTag) ? string.Empty : TextManager.Get(textTag);
}
}
public HUDProgressBar(Vector2 worldPosition, string textTag, Submarine parentSubmarine = null)
: this(worldPosition, parentSubmarine, GUI.Style.Red, GUI.Style.Green, textTag)
{
}
public HUDProgressBar(Vector2 worldPosition, Submarine parentSubmarine, Color emptyColor, Color fullColor)
public HUDProgressBar(Vector2 worldPosition, Submarine parentSubmarine, Color emptyColor, Color fullColor, string textTag)
{
this.emptyColor = emptyColor;
this.fullColor = fullColor;
parentSub = parentSubmarine;
WorldPosition = worldPosition;
Size = new Vector2(100.0f, 20.0f);
FadeTimer = 1.0f;
if (!string.IsNullOrEmpty(textTag))
{
textTag = textTag;
Text = TextManager.Get(textTag);
}
}
public void Update(float deltatime)
@@ -76,12 +94,21 @@ namespace Barotrauma
}
pos = cam.WorldToScreen(pos);
Color color = Color.Lerp(emptyColor, fullColor, progress);
GUI.DrawProgressBar(spriteBatch,
new Vector2(pos.X, -pos.Y),
Size, progress,
Color.Lerp(emptyColor, fullColor, progress) * a,
Size, progress,
color * a,
Color.White * a * 0.8f);
if (!string.IsNullOrEmpty(Text))
{
Vector2 textSize = GUI.SmallFont.MeasureString(Text);
Vector2 textPos = new Vector2(pos.X + (Size.X - textSize.X) / 2, pos.Y - textSize.Y * 1.2f);
GUI.DrawString(spriteBatch, textPos - Vector2.One, Text, Color.Black * a, font: GUI.SmallFont);
GUI.DrawString(spriteBatch, textPos, Text, Color.White * a, font: GUI.SmallFont);
}
}
}
}

View File

@@ -36,7 +36,7 @@ namespace Barotrauma
}
}
private GUIButton suicideButton;
public GUIButton SuicideButton { get; private set; }
// healthbars
private GUIProgressBar healthBar;
@@ -247,22 +247,6 @@ namespace Barotrauma
private GUIFrame healthBarHolder;
private Point healthBarOffset
{
get
{
return new Point(Math.Max(2, GUI.IntScaleCeiling(1.5f)), Math.Min(GUI.IntScaleFloor(18f), 19));
}
}
private Point healthBarSize
{
get
{
return new Point(healthBarHolder.Rect.Width - Math.Min(GUI.IntScale(45f), 47), GUI.IntScale(15f));
}
}
partial void InitProjSpecific(XElement element, Character character)
{
DisplayedVitality = MaxVitality;
@@ -290,29 +274,22 @@ namespace Barotrauma
healthBarHolder.RectTransform.NonScaledSize = HUDLayoutSettings.HealthBarArea.Size;
healthBarHolder.RectTransform.RelativeOffset = Vector2.Zero;
GUIFrame healthBarBG = new GUIFrame(new RectTransform(Vector2.One, healthBarHolder.RectTransform), style: "CharacterHealthBarBG")
{
CanBeFocused = false
};
healthBarShadow = new GUIProgressBar(new RectTransform(healthBarSize, healthBarHolder.RectTransform, Anchor.BottomRight),
barSize: 1.0f, color: Color.Green, style: horizontal ? "CharacterHealthBarSlider" : "GUIProgressBarVertical", showFrame: false)
healthBarShadow = new GUIProgressBar(new RectTransform(Vector2.One, healthBarHolder.RectTransform, Anchor.BottomRight),
barSize: 1.0f, color: Color.Green, style: horizontal ? "CharacterHealthBar" : "GUIProgressBarVertical", showFrame: false)
{
IsHorizontal = horizontal
};
healthBarShadow.Visible = false;
healthShadowSize = 1.0f;
healthBar = new GUIProgressBar(new RectTransform(healthBarSize, healthBarHolder.RectTransform, Anchor.BottomRight),
barSize: 1.0f, color: GUI.Style.HealthBarColorHigh, style: horizontal ? "CharacterHealthBarSlider" : "GUIProgressBarVertical", showFrame: false)
healthBar = new GUIProgressBar(new RectTransform(Vector2.One, healthBarHolder.RectTransform, Anchor.BottomRight),
barSize: 1.0f, color: GUI.Style.HealthBarColorHigh, style: horizontal ? "CharacterHealthBar" : "GUIProgressBarVertical")
{
HoverCursor = CursorState.Hand,
Enabled = true,
IsHorizontal = horizontal
};
healthBar.RectTransform.AbsoluteOffset = healthBarShadow.RectTransform.AbsoluteOffset = healthBarOffset;
healthInterfaceFrame = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.55f), GUI.Canvas, anchor: Anchor.Center, scaleBasis: ScaleBasis.Smallest), style: "ItemUI");
var healthInterfaceLayout = new GUILayoutGroup(new RectTransform(Vector2.One / 1.05f, healthInterfaceFrame.RectTransform, anchor: Anchor.Center), true);
@@ -519,7 +496,7 @@ namespace Barotrauma
UpdateAlignment();
suicideButton = new GUIButton(new RectTransform(new Vector2(0.1f, 0.02f), GUI.Canvas, Anchor.TopCenter)
SuicideButton = new GUIButton(new RectTransform(new Vector2(0.1f, 0.02f), GUI.Canvas, Anchor.TopCenter)
{
MinSize = new Point(150, 20), RelativeOffset = new Vector2(0.0f, 0.01f)
},
@@ -546,7 +523,7 @@ namespace Barotrauma
return true;
}
};
suicideButton.TextBlock.AutoScaleHorizontal = true;
SuicideButton.TextBlock.AutoScaleHorizontal = true;
if (element != null)
{
@@ -591,9 +568,6 @@ namespace Barotrauma
healthBarHolder.RectTransform.NonScaledSize = HUDLayoutSettings.HealthBarArea.Size;
healthBarHolder.RectTransform.RelativeOffset = Vector2.Zero;
healthBar.RectTransform.NonScaledSize = healthBarShadow.RectTransform.NonScaledSize = healthBarSize;
healthBar.RectTransform.AbsoluteOffset = healthBarShadow.RectTransform.AbsoluteOffset = healthBarOffset;
switch (alignment)
{
case Alignment.Left:
@@ -943,23 +917,7 @@ namespace Barotrauma
healthBar.State = GUIComponent.ComponentState.None;
}
suicideButton.Visible = Character == Character.Controlled && !Character.IsDead && Character.IsIncapacitated;
if (GameMain.GameSession?.Campaign is { } campaign)
{
RectTransform endRoundButton = campaign?.EndRoundButton.RectTransform;
if (endRoundButton != null)
{
if (suicideButton.Visible)
{
endRoundButton.ScreenSpaceOffset = new Point(0, suicideButton.Rect.Height);
}
else if (endRoundButton.ScreenSpaceOffset != Point.Zero)
{
endRoundButton.ScreenSpaceOffset = Point.Zero;
}
}
}
SuicideButton.Visible = Character == Character.Controlled && !Character.IsDead && Character.IsIncapacitated;
cprButton.Visible =
Character == Character.Controlled?.SelectedCharacter
@@ -992,9 +950,9 @@ namespace Barotrauma
{
healthBarHolder.AddToGUIUpdateList();
}
if (suicideButton.Visible && Character == Character.Controlled)
if (SuicideButton.Visible && Character == Character.Controlled)
{
suicideButton.AddToGUIUpdateList();
SuicideButton.AddToGUIUpdateList();
}
if (cprButton != null && cprButton.Visible)
{
@@ -1907,6 +1865,8 @@ namespace Barotrauma
private readonly List<Pair<AfflictionPrefab, float>> newAfflictions = new List<Pair<AfflictionPrefab, float>>();
private readonly List<Triplet<LimbHealth, AfflictionPrefab, float>> newLimbAfflictions = new List<Triplet<LimbHealth, AfflictionPrefab, float>>();
private readonly List<Pair<AfflictionPrefab.PeriodicEffect, float>> newPeriodicEffects = new List<Pair<AfflictionPrefab.PeriodicEffect, float>>();
public void ClientRead(IReadMessage inc)
{
newAfflictions.Clear();
@@ -1920,9 +1880,20 @@ namespace Barotrauma
DebugConsole.ThrowError("Error while reading character health data: affliction with the uint ID " + afflictionID + " not found.");
//read the 8 bytes for affliction strength anyway to prevent messing up reading rest of the message
_ = inc.ReadRangedSingle(0.0f, 100.0f, 8);
int _periodicAfflictionCount = inc.ReadByte();
for (int j = 0; j < _periodicAfflictionCount; j++)
{
_ = inc.ReadByte();
}
continue;
}
float afflictionStrength = inc.ReadRangedSingle(0.0f, afflictionPrefab.MaxStrength, 8);
int periodicAfflictionCount = inc.ReadByte();
for (int j = 0; j < periodicAfflictionCount; j++)
{
float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
newPeriodicEffects.Add(new Pair<AfflictionPrefab.PeriodicEffect, float>(afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
}
newAfflictions.Add(new Pair<AfflictionPrefab, float>(afflictionPrefab, afflictionStrength));
}
@@ -1940,12 +1911,26 @@ namespace Barotrauma
Affliction existingAffliction = afflictions.Find(a => a.Prefab == newAffliction.First);
if (existingAffliction == null)
{
afflictions.Add(newAffliction.First.Instantiate(newAffliction.Second));
existingAffliction = newAffliction.First.Instantiate(newAffliction.Second);
afflictions.Add(existingAffliction);
}
else
existingAffliction.SetStrength(newAffliction.Second);
if (existingAffliction == stunAffliction)
{
existingAffliction.Strength = newAffliction.Second;
if (existingAffliction == stunAffliction) Character.SetStun(existingAffliction.Strength, true, true);
Character.SetStun(existingAffliction.Strength, true, true);
}
foreach (var periodicEffect in newPeriodicEffects)
{
if (!existingAffliction.Prefab.PeriodicEffects.Contains(periodicEffect.First)) { continue; }
//timer has wrapped around, apply the effect
if (periodicEffect.Second - existingAffliction.PeriodicEffectTimers[periodicEffect.First] > periodicEffect.First.MinInterval / 2)
{
existingAffliction.PeriodicEffectTimers[periodicEffect.First] = periodicEffect.Second;
foreach (StatusEffect effect in periodicEffect.First.StatusEffects)
{
existingAffliction.ApplyStatusEffect(effect, deltaTime: 1.0f, this, targetLimb: null);
}
}
}
}
@@ -1961,9 +1946,20 @@ namespace Barotrauma
DebugConsole.ThrowError("Error while reading character health data: affliction with the uint ID " + afflictionID + " not found.");
//read the 8 bytes for affliction strength anyway to prevent messing up reading rest of the message
_ = inc.ReadRangedSingle(0.0f, 100.0f, 8);
int _periodicAfflictionCount = inc.ReadByte();
for (int j = 0; j < _periodicAfflictionCount; j++)
{
_ = inc.ReadByte();
}
continue;
}
float afflictionStrength = inc.ReadRangedSingle(0.0f, afflictionPrefab.MaxStrength, 8);
int periodicAfflictionCount = inc.ReadByte();
for (int j = 0; j < periodicAfflictionCount; j++)
{
float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
newPeriodicEffects.Add(new Pair<AfflictionPrefab.PeriodicEffect, float>(afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
}
newLimbAfflictions.Add(new Triplet<LimbHealth, AfflictionPrefab, float>(limbHealths[limbIndex], afflictionPrefab, afflictionStrength));
}
@@ -1980,15 +1976,28 @@ namespace Barotrauma
foreach (Triplet<LimbHealth, AfflictionPrefab, float> newAffliction in newLimbAfflictions)
{
if (newAffliction.First != limbHealth) continue;
if (newAffliction.First != limbHealth) { continue; }
Affliction existingAffliction = limbHealth.Afflictions.Find(a => a.Prefab == newAffliction.Second);
if (existingAffliction == null)
{
limbHealth.Afflictions.Add(newAffliction.Second.Instantiate(newAffliction.Third));
existingAffliction = newAffliction.Second.Instantiate(newAffliction.Third);
limbHealth.Afflictions.Add(existingAffliction);
}
else
existingAffliction.SetStrength(newAffliction.Third);
foreach (var periodicEffect in newPeriodicEffects)
{
existingAffliction.Strength = newAffliction.Third;
if (!existingAffliction.Prefab.PeriodicEffects.Contains(periodicEffect.First)) { continue; }
//timer has wrapped around, apply the effect
if (periodicEffect.Second - existingAffliction.PeriodicEffectTimers[periodicEffect.First] > periodicEffect.First.MinInterval / 2)
{
existingAffliction.PeriodicEffectTimers[periodicEffect.First] = periodicEffect.Second;
foreach (StatusEffect effect in periodicEffect.First.StatusEffects)
{
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == limbHealths.IndexOf(newAffliction.First));
existingAffliction.ApplyStatusEffect(effect, deltaTime: 1.0f, this, targetLimb: targetLimb);
}
}
}
}
}

View File

@@ -446,7 +446,7 @@ namespace Barotrauma
{
if (affliction is AfflictionBleeding)
{
bleedingDamage += affliction.GetVitalityDecrease(character.CharacterHealth);
bleedingDamage += affliction.GetVitalityDecrease(null);
}
}
}
@@ -455,7 +455,7 @@ namespace Barotrauma
{
if (affliction.Prefab.AfflictionType == "damage")
{
damage += affliction.GetVitalityDecrease(character.CharacterHealth);
damage += affliction.GetVitalityDecrease(null);
}
}
float damageMultiplier = 1;
@@ -488,7 +488,7 @@ namespace Barotrauma
}
// spawn damage particles
float damageParticleAmount = Math.Min(damage / 10, 1.0f) * damageMultiplier;
float damageParticleAmount = Math.Min(damage / 5, 1.0f) * damageMultiplier;
if (damageParticleAmount > 0.001f)
{
foreach (ParticleEmitter emitter in character.DamageEmitters)
@@ -510,11 +510,6 @@ namespace Barotrauma
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
emitter.Emit(1.0f, WorldPosition, character.CurrentHull, sizeMultiplier: bloodParticleSize, amountMultiplier: bloodParticleAmount);
}
if (bloodParticleAmount > 0 && character.CurrentHull != null && !string.IsNullOrEmpty(character.BloodDecalName))
{
character.CurrentHull.AddDecal(character.BloodDecalName, WorldPosition, MathHelper.Clamp(bloodParticleSize, 0.5f, 1.0f));
}
}
}

View File

@@ -1086,17 +1086,6 @@ namespace Barotrauma
AssignRelayToServer("water|editwater", false);
AssignRelayToServer("fire|editfire", false);
commands.Add(new Command("togglecampaignteleport", "togglecampaignteleport: Toggle on/off teleportation between campaign locations by double clicking on the campaign map.", (string[] args) =>
{
if (GameMain.GameSession?.Campaign == null)
{
ThrowError("No campaign active.");
return;
}
GameMain.GameSession.Map.AllowDebugTeleport = !GameMain.GameSession.Map.AllowDebugTeleport;
NewMessage((GameMain.GameSession.Map.AllowDebugTeleport ? "Enabled" : "Disabled") + " teleportation on the campaign map.", Color.White);
}, isCheat: true));
commands.Add(new Command("mute", "mute [name]: Prevent the client from speaking to anyone through the voice chat. Using this command requires a permission from the server host.",
null,
() =>
@@ -1136,7 +1125,7 @@ namespace Barotrauma
{
foreach (var ingredient in fabricationRecipe.RequiredItems)
{
int? ingredientPrice = ingredient.ItemPrefab.GetMinPrice();
int? ingredientPrice = ingredient.ItemPrefabs.Min(ip => ip.GetMinPrice());
if (ingredientPrice.HasValue)
{
if (!fabricationCost.HasValue) { fabricationCost = 0; }
@@ -1163,14 +1152,14 @@ namespace Barotrauma
if (fabricationRecipe != null)
{
var ingredient = fabricationRecipe.RequiredItems.Find(r => r.ItemPrefab == targetItem);
var ingredient = fabricationRecipe.RequiredItems.Find(r => r.ItemPrefabs.Contains(targetItem));
if (ingredient == null)
{
NewMessage("Deconstructing \"" + itemPrefab.Name + "\" produces \"" + deconstructItem.ItemIdentifier + "\", which isn't required in the fabrication recipe of the item.", Color.Red);
}
else if (ingredient.UseCondition && ingredient.MinCondition < deconstructItem.OutCondition)
{
NewMessage($"Deconstructing \"{itemPrefab.Name}\" produces more \"{deconstructItem.ItemIdentifier}\", than what's required to fabricate the item (required: {ingredient.ItemPrefab.Name} {(int)(ingredient.MinCondition * 100)}%, output: {deconstructItem.ItemIdentifier} {(int)(deconstructItem.OutCondition * 100)}%)", Color.Red);
NewMessage($"Deconstructing \"{itemPrefab.Name}\" produces more \"{deconstructItem.ItemIdentifier}\", than what's required to fabricate the item (required: {targetItem.Name} {(int)(ingredient.MinCondition * 100)}%, output: {deconstructItem.ItemIdentifier} {(int)(deconstructItem.OutCondition * 100)}%)", Color.Red);
}
}
}
@@ -1389,6 +1378,39 @@ namespace Barotrauma
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
}));
#if DEBUG
commands.Add(new Command("setplanthealth", "setplanthealth [value]: Sets the health of the selected plant in sub editor.", (string[] args) =>
{
if (1 > args.Length || Screen.Selected != GameMain.SubEditorScreen) { return; }
string arg = args[0];
if (!float.TryParse(arg, out float value))
{
ThrowError($"{arg} is not a valid value.");
return;
}
foreach (MapEntity me in MapEntity.SelectedList)
{
if (me is Item it)
{
if (it.GetComponent<Planter>() is { } planter)
{
foreach (Growable seed in planter.GrowableSeeds.Where(s => s != null))
{
NewMessage($"Set the health of {seed.Name} to {value} (from {seed.Health})");
seed.Health = value;
}
}
else if (it.GetComponent<Growable>() is { } seed)
{
NewMessage($"Set the health of {seed.Name} to {value} (from {seed.Health})");
seed.Health = value;
}
}
}
}));
commands.Add(new Command("printreceivertransfers", "", (string[] args) =>
{
GameMain.Client.PrintReceiverTransters();
@@ -2012,7 +2034,7 @@ namespace Barotrauma
return;
}
GameMain.Config.SelectCorePackage(GameMain.Config.SelectedContentPackages.First(cp => cp.CorePackage), true);
GameMain.Config.SelectCorePackage(GameMain.Config.CurrentCorePackage, true);
}));
commands.Add(new Command("ingamemodswap", "", (string[] args) =>
@@ -2041,7 +2063,7 @@ namespace Barotrauma
}
ShowQuestionPrompt("Permission to grant to client " + args[0] + "?", (perm) =>
{
GameMain.Client?.SendConsoleCommand("giveperm " + args[0] + " " + perm);
GameMain.Client?.SendConsoleCommand("giveperm \"" + args[0] + "\" " + perm);
}, args, 1);
}
);
@@ -2060,7 +2082,7 @@ namespace Barotrauma
ShowQuestionPrompt("Permission to revoke from client " + args[0] + "?", (perm) =>
{
GameMain.Client?.SendConsoleCommand("revokeperm " + args[0] + " " + perm);
GameMain.Client?.SendConsoleCommand("revokeperm \"" + args[0] + "\" " + perm);
}, args, 1);
}
);
@@ -2078,7 +2100,7 @@ namespace Barotrauma
}
ShowQuestionPrompt("Rank to grant to client " + args[0] + "?", (rank) =>
{
GameMain.Client?.SendConsoleCommand("giverank " + args[0] + " " + rank);
GameMain.Client?.SendConsoleCommand("giverank \"" + args[0] + "\" " + rank);
}, args, 1);
}
);
@@ -2091,7 +2113,7 @@ namespace Barotrauma
ShowQuestionPrompt("Console command permissions to grant to client " + args[0] + "? You may enter multiple commands separated with a space or use \"all\" to give the permission to use all console commands.", (commandNames) =>
{
GameMain.Client?.SendConsoleCommand("givecommandperm " + args[0] + " " + commandNames);
GameMain.Client?.SendConsoleCommand("givecommandperm \"" + args[0] + "\" " + commandNames);
}, args, 1);
}
);
@@ -2104,7 +2126,7 @@ namespace Barotrauma
ShowQuestionPrompt("Console command permissions to revoke from client " + args[0] + "? You may enter multiple commands separated with a space or use \"all\" to revoke the permission to use any console commands.", (commandNames) =>
{
GameMain.Client?.SendConsoleCommand("revokecommandperm " + args[0] + " " + commandNames);
GameMain.Client?.SendConsoleCommand("revokecommandperm \"" + args[0] + "\" " + commandNames);
}, args, 1);
}
);
@@ -2505,7 +2527,7 @@ namespace Barotrauma
}
}, isCheat: true));
commands.Add(new Command("spawnsub", "spawnsub [subname]: Spawn a submarine at the position of the cursor", (string[] args) =>
commands.Add(new Command("spawnsub", "spawnsub [subname] [is thalamus]: Spawn a submarine at the position of the cursor", (string[] args) =>
{
if (GameMain.NetworkMember != null)
{
@@ -2528,6 +2550,25 @@ namespace Barotrauma
{
Submarine spawnedSub = Submarine.Load(subInfo, false);
spawnedSub.SetPosition(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition));
if (subInfo.Type == SubmarineType.Wreck)
{
spawnedSub.MakeWreck();
if (args.Length > 1 && bool.TryParse(args[1], out bool isThalamus))
{
if (isThalamus)
{
spawnedSub.CreateWreckAI();
}
else
{
spawnedSub.DisableWreckAI();
}
}
else
{
spawnedSub.DisableWreckAI();
}
}
}
}
catch (Exception e)

View File

@@ -0,0 +1,33 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
namespace Barotrauma
{
partial class Decal
{
public void Draw(SpriteBatch spriteBatch, Hull hull, float depth)
{
Vector2 drawPos = position + hull.Rect.Location.ToVector2();
if (hull.Submarine != null) { drawPos += hull.Submarine.DrawPosition; }
drawPos.Y = -drawPos.Y;
spriteBatch.Draw(Sprite.Texture, drawPos, clippedSourceRect, Color * GetAlpha(), 0, Vector2.Zero, Scale, SpriteEffects.None, depth);
if (GameMain.DebugDraw && affectedSections != null && affectedSections.Count > 0)
{
Vector2 drawOffset = hull.Submarine == null ? Vector2.Zero : hull.Submarine.DrawPosition;
Point sectionSize = affectedSections.First().Rect.Size;
Rectangle drawPositionRect = new Rectangle((int)(drawOffset.X + hull.Rect.X), (int)(drawOffset.Y + hull.Rect.Y), sectionSize.X, sectionSize.Y);
foreach (var section in affectedSections)
{
// Draw colors
GUI.DrawRectangle(spriteBatch, new Vector2(drawPositionRect.X + section.Rect.X, -(drawPositionRect.Y + section.Rect.Y)), new Vector2(sectionSize.X, sectionSize.Y), Color.Red, false, 0.0f, (int)Math.Max(1.5f / Screen.Selected.Cam.Zoom, 1.0f));
}
}
}
}
}

View File

@@ -67,7 +67,7 @@ namespace Barotrauma
GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox;
Debug.Assert(conversationList != null);
// gray out the last text block
if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement)
{
@@ -132,17 +132,19 @@ namespace Barotrauma
};
shadow.SetAsFirstChild();
void RecalculateLastMessage(GUIListBox conversationList, bool append)
static void RecalculateLastMessage(GUIListBox conversationList, bool append)
{
if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement)
{
GUILayoutGroup textLayout = lastElement.GetChild<GUILayoutGroup>();
if (lastElement.Rect.Size.Y < textLayout.Rect.Size.Y && !append)
{
lastElement.RectTransform.MinSize = textLayout.Rect.Size;
}
if (textLayout != null)
{
if (lastElement.Rect.Size.Y < textLayout.Rect.Size.Y && !append)
{
lastElement.RectTransform.MinSize = textLayout.Rect.Size;
}
int textHeight = textLayout.Children.Sum(c => c.Rect.Height);
textLayout.RectTransform.MaxSize = new Point(lastElement.RectTransform.MaxSize.X, textHeight);
textLayout.Recalculate();
@@ -323,7 +325,10 @@ namespace Barotrauma
}
textContent.RectTransform.MinSize = new Point(0, textContent.Children.Sum(c => c.Rect.Height + textContent.AbsoluteSpacing) + GUI.IntScale(16));
// content.RectTransform.MinSize = new Point(0, textContent.Rect.Height);
// Recalculate the text size as it is scaled up and no longer matching the text height due to the textContent's minSize increasing
textBlock.CalculateHeightFromText();
//content.RectTransform.MinSize = new Point(0, textContent.Rect.Height);
return buttons;
}

View File

@@ -17,10 +17,11 @@ namespace Barotrauma
private float intensityGraphUpdateInterval;
private float lastIntensityUpdate;
private Event? pinnedEvent;
private Vector2 pinnedPosition = new Vector2(256, 128);
private bool isDragging;
public Event? PinnedEvent { get; set; }
public void DebugDraw(SpriteBatch spriteBatch)
{
foreach (Event ev in activeEvents)
@@ -104,12 +105,21 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "New event (ID " + eventSet.DebugIdentifier + ") after: ", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
if ((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) &&
roundDuration < eventSet.MinMissionTime)
if (eventSet.PerWreck)
{
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), " submarine near the wreck", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
}
if (eventSet.PerRuin)
{
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), " submarine near the ruins", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
}
if (roundDuration < eventSet.MinMissionTime)
{
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y),
" " + (int) (eventSet.MinDistanceTraveled * 100.0f) + "% travelled (current: " + (int) (distanceTraveled * 100.0f) + " %)",
Color.Orange * 0.8f, null, 0, GUI.SmallFont);
((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) ? Color.Lerp(GUI.Style.Yellow, GUI.Style.Red, eventSet.MinDistanceTraveled - distanceTraveled) : GUI.Style.Green) * 0.8f, null, 0, GUI.SmallFont);
y += 12;
}
@@ -125,7 +135,7 @@ namespace Barotrauma
{
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y),
" " + (int) (eventSet.MinMissionTime - roundDuration) + " s",
Color.Orange * 0.8f, null, 0, GUI.SmallFont);
Color.Lerp(GUI.Style.Yellow, GUI.Style.Red, (eventSet.MinMissionTime - roundDuration)), null, 0, GUI.SmallFont);
}
y += 15;
@@ -143,25 +153,25 @@ namespace Barotrauma
Rectangle outlineRect = new Rectangle(rect.Location, rect.Size);
outlineRect.Inflate(4, 4);
if (pinnedEvent == ev) { GUI.DrawRectangle(spriteBatch, outlineRect, Color.White); }
if (PinnedEvent == ev) { GUI.DrawRectangle(spriteBatch, outlineRect, Color.White); }
if (rect.Contains(PlayerInput.MousePosition))
{
GUI.MouseCursor = CursorState.Hand;
GUI.DrawRectangle(spriteBatch, outlineRect, Color.White);
if (ev != pinnedEvent)
if (ev != PinnedEvent)
{
DrawEvent(spriteBatch, ev, rect);
}
else if (PlayerInput.SecondaryMouseButtonHeld() || PlayerInput.SecondaryMouseButtonDown())
{
pinnedEvent = null;
PinnedEvent = null;
}
if (PlayerInput.PrimaryMouseButtonHeld() || PlayerInput.PrimaryMouseButtonDown())
{
pinnedEvent = ev;
PinnedEvent = ev;
}
}
@@ -171,9 +181,9 @@ namespace Barotrauma
public void DrawPinnedEvent(SpriteBatch spriteBatch)
{
if (pinnedEvent != null)
if (PinnedEvent != null)
{
Rectangle rect = DrawEvent(spriteBatch, pinnedEvent, null);
Rectangle rect = DrawEvent(spriteBatch, PinnedEvent, null);
if (rect != Rectangle.Empty)
{
@@ -187,7 +197,7 @@ namespace Barotrauma
if (PlayerInput.SecondaryMouseButtonClicked() || PlayerInput.SecondaryMouseButtonHeld())
{
pinnedEvent = null;
PinnedEvent = null;
isDragging = false;
}
}

View File

@@ -2,11 +2,9 @@
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
@@ -14,7 +12,7 @@ namespace Barotrauma
{
public const string RadioChatString = "r; ";
private GUIListBox chatBox;
private readonly GUIListBox chatBox;
private Point screenResolution;
public readonly ChatManager ChatManager = new ChatManager();
@@ -37,10 +35,17 @@ namespace Barotrauma
private float prevUIScale;
private readonly GUIFrame channelSettingsFrame;
private readonly GUITextBox channelText;
private readonly GUILayoutGroup channelPickerContent;
private readonly GUIButton memButton;
private WifiComponent prevRadio;
private bool channelMemPending;
//individual message texts that pop up when the chatbox is hidden
const float PopupMessageDuration = 5.0f;
private float popupMessageTimer;
private Queue<GUIComponent> popupMessages = new Queue<GUIComponent>();
private readonly List<GUIComponent> popupMessages = new List<GUIComponent>();
public GUITextBox.OnEnterHandler OnEnterMessage
{
@@ -67,9 +72,9 @@ namespace Barotrauma
}
}
private GUIButton showNewMessagesButton;
private readonly GUIButton showNewMessagesButton;
private GUIFrame hideableElements;
private readonly GUIFrame hideableElements;
public const int ToggleButtonWidthRaw = 30;
private int popupMessageOffset;
@@ -88,6 +93,133 @@ namespace Barotrauma
var chatBoxHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.875f), hideableElements.RectTransform), style: "ChatBox");
chatBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.95f), chatBoxHolder.RectTransform, Anchor.CenterRight), style: null);
// channel settings -----------------------------------------------------------------------------
channelSettingsFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), chatBoxHolder.RectTransform, Anchor.TopCenter, Pivot.BottomCenter) { MinSize = new Point(0, 25) }, style: "GUIFrameBottom");
var channelSettingsContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), channelSettingsFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.01f
};
var buttonLeft = new GUIButton(new RectTransform(new Vector2(0.1f, 0.8f), channelSettingsContent.RectTransform), style: "DeviceButton")
{
OnClicked = (btn, userdata) =>
{
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{
SetChannel(radio.Channel - 1, setText: true);
GUI.PlayUISound(GUISoundType.PopupMenu);
}
return true;
}
};
var arrowIcon = new GUIImage(new RectTransform(new Vector2(0.4f), buttonLeft.RectTransform, Anchor.Center), style: "GUIButtonHorizontalArrow", scaleToFit: true)
{
Color = new Color(51, 59, 46),
SpriteEffects = Microsoft.Xna.Framework.Graphics.SpriteEffects.FlipHorizontally
};
arrowIcon.HoverColor = arrowIcon.PressedColor = arrowIcon.PressedColor = arrowIcon.Color;
channelText = new GUITextBox(new RectTransform(new Vector2(0.25f, 0.8f), channelSettingsContent.RectTransform), style: "DigitalFrameLight", textAlignment: Alignment.Center, font: GUI.DigitalFont)
{
textFilterFunction = text =>
{
var str = new string(text.Where(c => char.IsNumber(c)).ToArray());
if (str.Length > 4) { str = str.Substring(0, 4); }
return str;
},
OnEnterPressed = (tb, text) =>
{
tb.Deselect();
return true;
}
};
Vector2 textSize = channelText.Font.MeasureString("0000");
channelText.TextBlock.ToolTip = TextManager.Get("currentradiochannel");
channelText.TextBlock.TextScale = Math.Min(channelText.Rect.Height / textSize.Y * 0.9f, 1.0f);
channelText.OnDeselected += (sender, key) =>
{
int.TryParse(channelText.Text, out int newChannel);
SetChannel(newChannel, setText: true);
GUI.PlayUISound(GUISoundType.PopupMenu);
};
var buttonRight = new GUIButton(new RectTransform(new Vector2(0.1f, 0.8f), channelSettingsContent.RectTransform), style: "DeviceButton")
{
OnClicked = (btn, userdata) =>
{
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{
SetChannel(radio.Channel + 1, setText: true);
GUI.PlayUISound(GUISoundType.PopupMenu);
}
return true;
}
};
arrowIcon = new GUIImage(new RectTransform(new Vector2(0.4f), buttonRight.RectTransform, Anchor.Center), style: "GUIButtonHorizontalArrow", scaleToFit: true)
{
Color = new Color(51, 59, 46)
};
arrowIcon.HoverColor = arrowIcon.PressedColor = arrowIcon.PressedColor = arrowIcon.Color;
var channelPicker = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.6f), channelSettingsContent.RectTransform), "InnerFrame");
channelPickerContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), channelPicker.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true
};
for (int i = 0; i < 10; i++)
{
new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), channelPickerContent.RectTransform), i.ToString(), style: "GUITextBlock")
{
TextColor = new Color(51, 59, 46),
SelectedTextColor = GUI.Style.Green,
UserData = i,
OnClicked = (btn, userdata) =>
{
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{
int index = (int)userdata;
if (channelMemPending)
{
int.TryParse(channelText.Text, out int newChannel);
radio.SetChannelMemory(index, newChannel);
btn.ToolTip = TextManager.GetWithVariables("radiochannelpreset",
new string[] { "[index]", "[channel]" },
new string[] { index.ToString(), radio.GetChannelMemory(index).ToString() });
channelMemPending = false;
channelPickerContent.Children.First().CanBeFocused = true;
memButton.Enabled = true;
channelPickerContent.Flash(GUI.Style.Green);
channelText.Flash(GUI.Style.Green);
}
SetChannel(radio.GetChannelMemory(index), setText: true);
GUI.PlayUISound(GUISoundType.PopupMenu);
}
return true;
}
};
}
memButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.9f), channelSettingsContent.RectTransform), style: "DeviceButton", text: TextManager.Get("saveradiochannelbutton"))
{
ToolTip = TextManager.Get("saveradiochannelbuttontooltip"),
OnClicked = (btn, userdata) =>
{
channelMemPending = true;
//don't allow storing a value in the first preset
channelPickerContent.Children.First().CanBeFocused = false;
foreach (GUIComponent channelButton in channelPickerContent.Children)
{
channelButton.Selected = false;
}
btn.Enabled = false;
return true;
}
};
// ---------------------------------------------------------------------------------------------
InputBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.125f), hideableElements.RectTransform, Anchor.BottomLeft),
style: "ChatTextBox")
{
@@ -148,7 +280,7 @@ namespace Barotrauma
}
}
Color textColor = textBox.Color;
Color textColor;
switch (command)
{
case "r":
@@ -235,7 +367,7 @@ namespace Barotrauma
},
Text = senderName
};
senderNameBlock.RectTransform.NonScaledSize = senderNameBlock.TextBlock.TextSize.ToPoint();
senderNameBlock.TextBlock.OverrideTextColor(senderColor);
if (senderNameBlock.UserData != null)
@@ -244,7 +376,7 @@ namespace Barotrauma
}
}
var msgText =new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), msgHolder.RectTransform)
var msgText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), msgHolder.RectTransform)
{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), senderNameTimestamp == null ? 0 : senderNameTimestamp.Rect.Height) },
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null, wrap: true,
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f)
@@ -296,7 +428,7 @@ namespace Barotrauma
{
var popupMsg = new GUIFrame(new RectTransform(Vector2.One, GUIFrame.RectTransform), style: "GUIToolTip")
{
Visible = false,
UserData = 0.0f,
CanBeFocused = false
};
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), popupMsg.RectTransform, Anchor.Center));
@@ -322,10 +454,10 @@ namespace Barotrauma
popupMsg.RectTransform.Resize(new Point((int)(textWidth / content.RectTransform.RelativeSize.X) , (int)((senderTextSize.Y + msgSize.Y) / content.RectTransform.RelativeSize.Y)), resizeChildren: true);
popupMsg.RectTransform.IsFixedSize = true;
content.Recalculate();
popupMessages.Enqueue(popupMsg);
popupMessages.Add(popupMsg);
}
if ((prevSize == 1.0f && chatBox.BarScroll == 0.0f) || (prevSize < 1.0f && chatBox.BarScroll == 1.0f)) chatBox.BarScroll = 1.0f;
if ((prevSize == 1.0f && chatBox.BarScroll == 0.0f) || (prevSize < 1.0f && chatBox.BarScroll == 1.0f)) { chatBox.BarScroll = 1.0f; }
GUISoundType soundType = GUISoundType.ChatMessage;
if (message.Type == ChatMessageType.Radio)
@@ -372,7 +504,7 @@ namespace Barotrauma
GUIFrame.RectTransform.NonScaledSize -= new Point(toggleButtonWidth, 0);
GUIFrame.RectTransform.AbsoluteOffset += new Point(toggleButtonWidth, 0);
popupMessageOffset = GameMain.GameSession.CrewManager.ReportButtonFrame.Rect.Width + GUIFrame.Rect.Width + (int)(20 * GUI.Scale);
popupMessageOffset = GameMain.GameSession.CrewManager.ReportButtonFrame.Rect.Width + GUIFrame.Rect.Width + (int)(35 * GUI.Scale);
}
public void Update(float deltaTime)
@@ -394,22 +526,93 @@ namespace Barotrauma
ToggleButton.RectTransform.AbsoluteOffset = new Point(GUIFrame.Rect.Right, GUIFrame.Rect.Y + HUDLayoutSettings.ChatBoxArea.Height - ToggleButton.Rect.Height);
}
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{
if (prevRadio != radio)
{
foreach (GUIComponent presetButton in channelPickerContent.Children)
{
int index = (int)presetButton.UserData;
presetButton.ToolTip = TextManager.GetWithVariables("radiochannelpreset",
new string[] { "[index]", "[channel]" },
new string[] { index.ToString(), radio.GetChannelMemory(index).ToString() });
}
SetChannel(radio.Channel, setText: true);
prevRadio = radio;
}
if (channelMemPending)
{
if (channelPickerContent.FlashTimer <= 0)
{
channelPickerContent.Flash(GUI.Style.Green, flashRectInflate: new Vector2(GUI.Scale * 5.0f));
}
if (PlayerInput.PrimaryMouseButtonClicked() && !GUI.IsMouseOn(channelPickerContent))
{
channelPickerContent.Children.First().CanBeFocused = true;
channelMemPending = false;
memButton.Enabled = true;
SetChannel(radio.Channel, setText: true);
}
}
channelSettingsFrame.Visible = true;
}
else
{
channelSettingsFrame.Visible = false;
channelPickerContent.Children.First().CanBeFocused = true;
channelMemPending = false;
memButton.Enabled = true;
}
if (ToggleOpen)
{
openState += deltaTime * 5.0f;
//delete all popup messages when the chatbox is open
while (popupMessages.Count > 0)
foreach (var popupMsg in popupMessages)
{
var popupMsg = popupMessages.Dequeue();
popupMsg.Parent.RemoveChild(popupMsg);
}
popupMessages.Clear();
}
else
{
openState -= deltaTime * 5.0f;
int yOffset = 0;
foreach (var popupMsg in popupMessages)
{
float msgTimer = (float)popupMsg.UserData;
int targetYOffset = (int)MathHelper.Lerp(popupMsg.RectTransform.ScreenSpaceOffset.Y, yOffset, deltaTime * 10.0f);
if (popupMsg == popupMessages.First())
{
popupMsg.UserData = msgTimer + deltaTime * Math.Max(popupMessages.Count / 2, 1);
if (msgTimer > PopupMessageDuration)
{
//move the message out of the screen and delete it
popupMsg.RectTransform.ScreenSpaceOffset =
new Point((int)MathHelper.SmoothStep(popupMessageOffset, 10, (msgTimer - PopupMessageDuration) * 5.0f), targetYOffset);
if (msgTimer > PopupMessageDuration + 0.2f)
{
popupMsg.Parent?.RemoveChild(popupMsg);
}
}
}
if (msgTimer < PopupMessageDuration)
{
if (popupMsg != popupMessages.First()) { popupMsg.UserData = Math.Min(msgTimer + deltaTime, 1.0f); }
//move the message on the screen
popupMsg.RectTransform.ScreenSpaceOffset = new Point(
(int)MathHelper.SmoothStep(0, popupMessageOffset, msgTimer * 5.0f), targetYOffset);
}
yOffset += popupMsg.Rect.Height + GUI.IntScale(10);
}
popupMessages.RemoveAll(p => p.Parent == null);
//make the first popup message visible
var popupMsg = popupMessages.Count > 0 ? popupMessages.Peek() : null;
/*var popupMsg = popupMessages.Count > 0 ? popupMessages.Peek() : null;
if (popupMsg != null)
{
popupMsg.Visible = true;
@@ -433,7 +636,7 @@ namespace Barotrauma
popupMsg.RectTransform.ScreenSpaceOffset = new Point(
(int)MathHelper.SmoothStep(0, popupMessageOffset, popupMessageTimer * 5.0f), 0);
}
}
}*/
}
openState = MathHelper.Clamp(openState, 0.0f, 1.0f);
int hiddenBoxOffset = -(GUIFrame.Rect.Width);
@@ -441,5 +644,27 @@ namespace Barotrauma
new Point((int)MathHelper.SmoothStep(hiddenBoxOffset, 0, openState), 0);
hideableElements.Visible = openState > 0.0f;
}
private void SetChannel(int channel, bool setText)
{
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{
radio.Channel = channel;
if (setText)
{
string text = radio.Channel.ToString().PadLeft(4, '0');
if (channelText.Text != text) { channelText.Text = text; }
}
if (!channelMemPending)
{
foreach (GUIComponent channelButton in channelPickerContent.Children)
{
int buttonIndex = (int)channelButton.UserData;
channelButton.Selected = radio.GetChannelMemory(buttonIndex) == channel;
}
}
}
}
}
}

View File

@@ -323,7 +323,7 @@ namespace Barotrauma
if (Screen.Selected == GameMain.GameScreen)
{
yOffset = -HUDLayoutSettings.ChatBoxArea.Height;
yOffset = (int)(-HUDLayoutSettings.ChatBoxArea.Height * 1.2f);
watermarkRect.Y += yOffset;
}
@@ -609,7 +609,7 @@ namespace Barotrauma
if (Character.Controlled?.Inventory != null)
{
if (!Character.Controlled.LockHands && Character.Controlled.Stun < 0.1f && !Character.Controlled.IsDead)
if (Character.Controlled.Stun < 0.1f && !Character.Controlled.IsDead)
{
Inventory.DrawFront(spriteBatch);
}
@@ -1267,7 +1267,7 @@ namespace Barotrauma
Vector2 diff = worldPosition - cam.WorldViewCenter;
float dist = diff.Length();
float symbolScale = 64.0f / sprite.size.X;
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f);
if (dist > hideDist)
{
@@ -2089,8 +2089,8 @@ namespace Barotrauma
Stretch = true,
RelativeSpacing = 0.05f
};
var button = 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")
{
IgnoreLayoutGroups = true,
@@ -2098,12 +2098,12 @@ namespace Barotrauma
OnClicked = (btn, userdata) => { GameMain.Instance.ShowBugReporter(); return true; }
};
button = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuResume"))
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuResume"))
{
OnClicked = TogglePauseMenu
};
button = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuSettings"))
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuSettings"))
{
OnClicked = (btn, userData) =>
{
@@ -2117,8 +2117,8 @@ namespace Barotrauma
{
if (GameMain.GameSession.GameMode is SinglePlayerCampaign spMode)
{
button = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuRetry"));
button.OnClicked += (btn, userData) =>
var retryButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuRetry"));
retryButton.OnClicked += (btn, userData) =>
{
var msgBox = new GUIMessageBox("", TextManager.Get("PauseMenuRetryVerification"), new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") })
{
@@ -2131,6 +2131,7 @@ namespace Barotrauma
GUIMessageBox.MessageBoxes.Remove(GameMain.GameSession.RoundSummary.Frame);
}
GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData as string == "ConversationAction");
TogglePauseMenu(btn, userData);
GameMain.GameSession.LoadPreviousSave();
return true;
@@ -2144,24 +2145,57 @@ namespace Barotrauma
};
return true;
};
button = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuSaveQuit"))
var saveAndQuitButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuSaveQuit"))
{
UserData = "save"
};
button.OnClicked += QuitClicked;
button.OnClicked += TogglePauseMenu;
saveAndQuitButton.OnClicked += (btn, userdata) =>
{
//Only allow saving mid-round in outpost levels. Quitting in the middle of a mission reset progress to the start of the round.
if (GameMain.GameSession == null)
{
pauseMenuOpen = false;
}
else if (GameMain.GameSession?.Campaign == null || Level.IsLoadedOutpost)
{
pauseMenuOpen = false;
GameMain.QuitToMainMenu(save: true);
}
else
{
var msgBox = new GUIMessageBox("", TextManager.Get("PauseMenuSaveAndQuitVerification", fallBackTag: "pausemenuquitverification"), new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") })
{
UserData = "verificationprompt"
};
msgBox.Buttons[0].OnClicked = (_, userdata) =>
{
pauseMenuOpen = false;
GameMain.QuitToMainMenu(save: false);
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = (_, userdata) =>
{
pauseMenuOpen = false;
msgBox.Close();
return true;
};
}
return true;
};
}
else if (GameMain.GameSession.GameMode is TestGameMode)
{
button = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), text: TextManager.Get("PauseMenuReturnToEditor"))
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), text: TextManager.Get("PauseMenuReturnToEditor"))
{
OnClicked = (btn, userdata) =>
{
GameMain.GameSession.EndRound("");
pauseMenuOpen = false;
return true;
}
};
button.OnClicked += TogglePauseMenu;
}
else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ManageRound))
{
@@ -2181,7 +2215,7 @@ namespace Barotrauma
};
msgBox.Buttons[0].OnClicked = (_, __) =>
{
TogglePauseMenu(btn, userdata);
pauseMenuOpen = false;
GameMain.Client.RequestRoundEnd();
return true;
};
@@ -2190,7 +2224,7 @@ namespace Barotrauma
}
else
{
TogglePauseMenu(btn, userdata);
pauseMenuOpen = false;
GameMain.Client.RequestRoundEnd();
}
return true;
@@ -2199,10 +2233,9 @@ namespace Barotrauma
}
}
button = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuQuit"));
button.OnClicked += (btn, userData) =>
var quitButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuQuit"));
quitButton.OnClicked += (btn, userData) =>
{
var quitButton = button;
if (GameMain.GameSession != null || (Screen.Selected is CharacterEditorScreen || Screen.Selected is SubEditorScreen))
{
string text = GameMain.GameSession == null ? "PauseMenuQuitVerificationEditor" : "PauseMenuQuitVerification";
@@ -2212,21 +2245,21 @@ namespace Barotrauma
};
msgBox.Buttons[0].OnClicked = (yesBtn, userdata) =>
{
QuitClicked(quitButton, quitButton.UserData);
GameMain.QuitToMainMenu(save: false);
pauseMenuOpen = false;
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = (_, userdata) =>
{
TogglePauseMenu(btn, userData);
pauseMenuOpen = false;
msgBox.Close();
return true;
};
}
else
{
QuitClicked(quitButton, quitButton.UserData);
GameMain.QuitToMainMenu(save: false);
pauseMenuOpen = false;
}
return true;
@@ -2242,12 +2275,6 @@ namespace Barotrauma
return true;
}
public static bool QuitClicked(GUIButton button, object obj)
{
GameMain.QuitToMainMenu(button.UserData as string == "save");
return true;
}
/// <summary>
/// Displays a message at the center of the screen, automatically preventing overlapping with other centered messages. TODO: Allow to show messages at the middle of the screen (instead of the top center).
/// </summary>

View File

@@ -104,6 +104,12 @@ namespace Barotrauma
set { textBlock.HoverTextColor = value; }
}
public Color SelectedTextColor
{
get { return textBlock.SelectedTextColor; }
set { textBlock.SelectedTextColor = value; }
}
public override float FlashTimer
{
get { return Frame.FlashTimer; }

View File

@@ -756,9 +756,9 @@ namespace Barotrauma
flashColor = (color == null) ? GUI.Style.Red : (Color)color;
}
public void FadeOut(float duration, bool removeAfter)
public void FadeOut(float duration, bool removeAfter, float wait = 0.0f)
{
CoroutineManager.StartCoroutine(LerpAlpha(0.0f, duration, removeAfter));
CoroutineManager.StartCoroutine(LerpAlpha(0.0f, duration, removeAfter, wait));
}
public void FadeIn(float wait, float duration)

View File

@@ -239,7 +239,7 @@ namespace Barotrauma
{
activeTextureLoads.Add(Sprite.FullPath);
}
Sprite.EnsureLazyLoaded();
await Sprite.LazyLoadAsync();
}
finally
{

View File

@@ -446,7 +446,7 @@ namespace Barotrauma
//dragging
if (CanDragElements && draggedElement != null)
{
if (!PlayerInput.LeftButtonHeld())
if (!PlayerInput.PrimaryMouseButtonHeld())
{
OnRearranged?.Invoke(this, draggedElement.UserData);
draggedElement = null;
@@ -533,7 +533,7 @@ namespace Barotrauma
}
}
if (CanDragElements && PlayerInput.LeftButtonDown() && GUI.MouseOn == child)
if (CanDragElements && PlayerInput.PrimaryMouseButtonDown() && GUI.MouseOn == child)
{
draggedElement = child;
draggedReferenceRectangle = child.Rect;
@@ -970,7 +970,6 @@ namespace Barotrauma
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
}
var children = Content.Children;
int lastVisible = 0;
int i = 0;

View File

@@ -180,13 +180,13 @@ namespace Barotrauma
Icon = new GUIImage(new RectTransform(new Vector2(0.2f, 0.95f), horizontalLayoutGroup.RectTransform), iconStyle, scaleToFit: true);
}
Content = new GUILayoutGroup(new RectTransform(new Vector2(icon != null ? 0.65f : 0.85f, 1.0f), horizontalLayoutGroup.RectTransform));
Content = new GUILayoutGroup(new RectTransform(new Vector2(Icon != null ? 0.65f : 0.85f, 1.0f), horizontalLayoutGroup.RectTransform));
var buttonContainer = new GUIFrame(new RectTransform(new Vector2(0.15f, 1.0f), horizontalLayoutGroup.RectTransform), style: null);
Buttons = new List<GUIButton>(1)
{
new GUIButton(new RectTransform(new Vector2(0.5f, 0.5f), buttonContainer.RectTransform, Anchor.Center),
style: "GUIButtonHorizontalArrow")
new GUIButton(new RectTransform(new Vector2(0.3f, 0.5f), buttonContainer.RectTransform, Anchor.Center),
style: "UIToggleButton")
{
OnClicked = Close
}
@@ -221,7 +221,7 @@ namespace Barotrauma
Content.RectTransform.NonScaledSize =
new Point(Content.Rect.Width, height);
}
Buttons[0].RectTransform.MaxSize = new Point(Math.Min(Buttons[0].Rect.Width, Buttons[0].Rect.Height));
Buttons[0].RectTransform.MaxSize = new Point((int)(0.4f * Buttons[0].Rect.Y), Buttons[0].Rect.Y);
}
MessageBoxes.Add(this);
@@ -271,99 +271,92 @@ namespace Barotrauma
protected override void Update(float deltaTime)
{
if (type == Type.InGame)
if (type != Type.InGame) { return; }
Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
if (!closing)
{
Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
/*for (int i = MessageBoxes.IndexOf(this); i >= 0; i--)
Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
if (MessageBoxes[i] is GUIMessageBox otherMsgBox && otherMsgBox != this && otherMsgBox.type == type && !otherMsgBox.closing)
BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2);
if (!MathUtils.NearlyEqual(openState, 1.0f))
{
defaultPos = new Vector2(
Math.Max(otherMsgBox.InnerFrame.RectTransform.AbsoluteOffset.X + 10 * GUI.Scale, defaultPos.X),
Math.Max(otherMsgBox.InnerFrame.RectTransform.AbsoluteOffset.Y + 10 * GUI.Scale, defaultPos.Y));
}
}*/
if (!closing)
{
Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2);
if (!MathUtils.NearlyEqual(openState, 1.0f))
{
BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White);
}
}
openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose)
{
inGameCloseTimer += deltaTime;
}
if (inGameCloseTimer >= inGameCloseTime)
{
Close();
BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White);
}
}
else
if (!(Screen.Selected is RoundSummaryScreen) && !MessageBoxes.Any(mb => mb.UserData is RoundSummary))
{
openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
}
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose)
{
inGameCloseTimer += deltaTime;
}
if (inGameCloseTimer >= inGameCloseTime)
{
Close();
}
}
else
{
openState += deltaTime * 2.0f;
Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
}
if (openState >= 2.0f)
{
if (Parent != null) { Parent.RemoveChild(this); }
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
}
if (newBackgroundIcon != null)
{
if (!iconSwitching)
{
openState += deltaTime * 2.0f;
Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
}
if (openState >= 2.0f)
{
if (Parent != null) { Parent.RemoveChild(this); }
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
}
if (newBackgroundIcon != null)
{
if (!iconSwitching)
{
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
if (BackgroundIcon.Color.A == 0)
{
BackgroundIcon = null;
iconSwitching = true;
RemoveChild(BackgroundIcon);
}
}
else
if (BackgroundIcon.Color.A == 0)
{
BackgroundIcon = null;
iconSwitching = true;
RemoveChild(BackgroundIcon);
}
iconState = 0;
}
else
{
newBackgroundIcon.SetAsFirstChild();
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White);
if (newBackgroundIcon.Color.A == 255)
{
BackgroundIcon = newBackgroundIcon;
BackgroundIcon.SetAsFirstChild();
newBackgroundIcon = null;
iconSwitching = false;
}
iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f);
iconSwitching = true;
}
iconState = 0;
}
else
{
newBackgroundIcon.SetAsFirstChild();
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White);
if (newBackgroundIcon.Color.A == 255)
{
BackgroundIcon = newBackgroundIcon;
BackgroundIcon.SetAsFirstChild();
newBackgroundIcon = null;
iconSwitching = false;
}
iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f);
}
}
}

View File

@@ -380,16 +380,18 @@ namespace Barotrauma
private void ClampIntValue()
{
if (MinValueInt != null)
if (MinValueInt != null && intValue < MinValueInt.Value)
{
intValue = Math.Max(intValue, MinValueInt.Value);
minusButton.Enabled = intValue > MinValueInt;
UpdateText();
}
if (MaxValueInt != null)
if (MaxValueInt != null && intValue > MaxValueInt.Value)
{
intValue = Math.Min(intValue, MaxValueInt.Value);
plusButton.Enabled = intValue < MaxValueInt;
UpdateText();
}
plusButton.Enabled = intValue < MaxValueInt;
minusButton.Enabled = intValue > MinValueInt;
}
private void UpdateText()

View File

@@ -462,7 +462,7 @@ namespace Barotrauma
protected List<Tuple<Vector2, int>> GetAllPositions()
{
float halfHeight = Font.MeasureString("T").Y * 0.5f;
float halfHeight = Font.MeasureString("T").Y * 0.5f * textScale;
string textDrawn = Censor ? CensoredText : WrappedText;
var positions = new List<Tuple<Vector2, int>>();
if (textDrawn.Contains("\n"))
@@ -474,10 +474,10 @@ namespace Barotrauma
{
string line = lines[i];
totalIndex += line.Length;
float totalTextHeight = Font.MeasureString(textDrawn.Substring(0, totalIndex)).Y;
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));
Vector2 lineTextSize = Font.MeasureString(line.Substring(0, j)) * textScale;
Vector2 indexPos = new Vector2(lineTextSize.X + Padding.X, totalTextHeight + Padding.Y - halfHeight);
//DebugConsole.NewMessage($"index: {index}, pos: {indexPos}", Color.AliceBlue);
positions.Add(new Tuple<Vector2, int>(indexPos, index + j));
@@ -490,8 +490,8 @@ namespace Barotrauma
textDrawn = Censor ? CensoredText : Text;
for (int i = 0; i <= Text.Length; i++)
{
Vector2 textSize = Font.MeasureString(textDrawn.Substring(0, i));
Vector2 indexPos = new Vector2(textSize.X + Padding.X, textSize.Y + Padding.Y - halfHeight) + TextPos - Origin;
Vector2 textSize = Font.MeasureString(textDrawn.Substring(0, i)) * textScale;
Vector2 indexPos = new Vector2(textSize.X + Padding.X, textSize.Y + Padding.Y - halfHeight) + TextPos - Origin * textScale;
//DebugConsole.NewMessage($"index: {i}, pos: {indexPos}", Color.WhiteSmoke);
positions.Add(new Tuple<Vector2, int>(indexPos, i));
}
@@ -508,7 +508,7 @@ namespace Barotrauma
{
var positions = GetAllPositions();
if (positions.Count == 0) { return 0; }
float halfHeight = Font.MeasureString("T").Y * 0.5f;
float halfHeight = Font.MeasureString("T").Y * 0.5f * textScale;
var currPosition = positions[0];
@@ -522,7 +522,8 @@ namespace Barotrauma
float diffY = Math.Abs(p1.Item1.Y - pos.Y) - Math.Abs(p2.Item1.Y - pos.Y);
if (diffY < -3.0f)
{
currPosition = p1; continue;
currPosition = p1;
continue;
}
else if (diffY > 3.0f)
{

View File

@@ -325,7 +325,7 @@ namespace Barotrauma
}
else
{
while (ClampText && textBlock.Text.Length>0 && Font.MeasureString(textBlock.Text).X > (int)(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z))
while (ClampText && textBlock.Text.Length > 0 && Font.MeasureString(textBlock.Text).X * TextBlock.TextScale > (int)(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z))
{
textBlock.Text = textBlock.Text.Substring(0, textBlock.Text.Length - 1);
}
@@ -354,10 +354,10 @@ namespace Barotrauma
{
int diff = totalIndex - CaretIndex;
int index = currentLineLength - diff;
Vector2 lineTextSize = Font.MeasureString(lines[i].Substring(0, index));
Vector2 lastLineSize = Font.MeasureString(lines[i]);
float totalTextHeight = Font.MeasureString(textDrawn.Substring(0, totalIndex)).Y;
caretPos = new Vector2(lineTextSize.X, totalTextHeight - lastLineSize.Y) + textBlock.TextPos - textBlock.Origin;
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;
}
}
@@ -366,8 +366,8 @@ namespace Barotrauma
{
CaretIndex = Math.Min(CaretIndex, textDrawn.Length);
textDrawn = Censor ? textBlock.CensoredText : textBlock.Text;
Vector2 textSize = Font.MeasureString(textDrawn.Substring(0, CaretIndex));
caretPos = new Vector2(textSize.X, 0) + textBlock.TextPos - textBlock.Origin;
Vector2 textSize = Font.MeasureString(textDrawn.Substring(0, CaretIndex)) * TextBlock.TextScale;
caretPos = new Vector2(textSize.X, 0) + textBlock.TextPos - textBlock.Origin * TextBlock.TextScale;
}
caretPosDirty = false;
}
@@ -506,7 +506,7 @@ namespace Barotrauma
{
GUI.DrawLine(spriteBatch,
new Vector2(Rect.X + (int)caretPos.X + 2, Rect.Y + caretPos.Y + 3),
new Vector2(Rect.X + (int)caretPos.X + 2, Rect.Y + caretPos.Y + Font.MeasureString("I").Y - 3),
new Vector2(Rect.X + (int)caretPos.X + 2, Rect.Y + caretPos.Y + Font.MeasureString("I").Y * textBlock.TextScale - 3),
CaretColor ?? textBlock.TextColor * (textBlock.TextColor.A / 255.0f));
}
if (selectedCharacters > 0)
@@ -544,7 +544,7 @@ namespace Barotrauma
: selectionEndIndex < totalIndex && selectionStartIndex > previousCharacters;
if (containsSelection)
{
Vector2 currentLineSize = Font.MeasureString(currentLine);
Vector2 currentLineSize = Font.MeasureString(currentLine) * TextBlock.TextScale;
if ((IsLeftToRight && selectionStartIndex < previousCharacters && selectionEndIndex > totalIndex)
|| !IsLeftToRight && selectionEndIndex < previousCharacters && selectionStartIndex > totalIndex)
{
@@ -560,7 +560,7 @@ namespace Barotrauma
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));
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);
@@ -573,7 +573,7 @@ namespace Barotrauma
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));
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);
@@ -612,7 +612,7 @@ namespace Barotrauma
OnTextChanged?.Invoke(this, Text);
if (textBlock.OverflowClipActive && wasOverflowClipActive && !MathUtils.NearlyEqual(textBlock.TextPos, textPos))
{
textBlock.TextPos = textPos + Vector2.UnitX * Font.MeasureString(input).X;
textBlock.TextPos = textPos + Vector2.UnitX * Font.MeasureString(input).X * TextBlock.TextScale;
}
}
}
@@ -714,8 +714,8 @@ namespace Barotrauma
{
InitSelectionStart();
}
float lineHeight = Font.MeasureString("T").Y;
int newIndex = textBlock.GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y-lineHeight));
float lineHeight = Font.MeasureString("T").Y * TextBlock.TextScale;
int newIndex = textBlock.GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y - lineHeight));
CaretIndex = newIndex;
caretTimer = 0;
HandleSelection();
@@ -725,8 +725,8 @@ namespace Barotrauma
{
InitSelectionStart();
}
lineHeight = Font.MeasureString("T").Y;
newIndex = textBlock.GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y+lineHeight));
lineHeight = Font.MeasureString("T").Y * TextBlock.TextScale;
newIndex = textBlock.GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y + lineHeight));
CaretIndex = newIndex;
caretTimer = 0;
HandleSelection();
@@ -865,18 +865,18 @@ namespace Barotrauma
{
string textDrawn = Censor ? textBlock.CensoredText : textBlock.WrappedText;
InitSelectionStart();
selectionEndIndex = CaretIndex;
selectionEndIndex = Math.Min(CaretIndex, textDrawn.Length);
selectionEndPos = caretPos;
selectedCharacters = Math.Abs(selectionStartIndex - selectionEndIndex);
if (IsLeftToRight)
{
selectedText = Text.Substring(selectionStartIndex, selectedCharacters);
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionStartIndex, selectedCharacters));
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionStartIndex, selectedCharacters)) * TextBlock.TextScale;
}
else
{
selectedText = Text.Substring(selectionEndIndex, selectedCharacters);
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionEndIndex, selectedCharacters));
selectedText = Text.Substring(selectionEndIndex, Math.Min(selectedCharacters, textDrawn.Length - selectionEndIndex));
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionEndIndex, selectedCharacters)) * TextBlock.TextScale;
}
}
}

View File

@@ -68,6 +68,10 @@ namespace Barotrauma
CreateUI();
campaignUI.Campaign.Map.OnLocationChanged += UpdateLocation;
if (CurrentLocation?.Reputation != null)
{
CurrentLocation.Reputation.OnReputationValueChanged += Refresh;
}
campaignUI.Campaign.CargoManager.OnItemsInBuyCrateChanged += RefreshBuying;
campaignUI.Campaign.CargoManager.OnPurchasedItemsChanged += RefreshBuying;
campaignUI.Campaign.CargoManager.OnItemsInSellCrateChanged += RefreshSelling;
@@ -378,9 +382,13 @@ namespace Barotrauma
{
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), totalContainer.RectTransform), TextManager.Get("campaignstore.total"), font: GUI.Font);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), totalContainer.RectTransform), TextManager.Get("campaignstore.total"), font: GUI.Font)
{
CanBeFocused = false
};
shoppingCrateTotal = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), totalContainer.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
{
CanBeFocused = false,
TextScale = 1.1f
};
@@ -412,11 +420,20 @@ namespace Barotrauma
{
if (prevLocation == newLocation) { return; }
if (prevLocation?.Reputation != null)
{
prevLocation.Reputation.OnReputationValueChanged -= Refresh;
}
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
{
if (itemPrefab.CanBeBoughtAtLocation(CurrentLocation, out PriceInfo _))
{
ChangeStoreTab(StoreTab.Buy);
if (newLocation?.Reputation != null)
{
newLocation.Reputation.OnReputationValueChanged += Refresh;
}
return;
}
}
@@ -717,9 +734,15 @@ namespace Barotrauma
private GUIComponent CreateItemFrame(PurchasedItem pi, PriceInfo priceInfo, GUIListBox listBox, bool forceDisable = false)
{
var tooltip = pi.ItemPrefab.Name;
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
{
tooltip += "\n" + pi.ItemPrefab.Description;
}
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 60)), parent: listBox.Content.RectTransform), style: "ListBoxElement")
{
ToolTip = pi.ItemPrefab.Description,
ToolTip = tooltip,
UserData = pi
};
@@ -740,6 +763,7 @@ namespace Barotrauma
iconRelativeWidth = (0.9f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
GUIImage img = new GUIImage(new RectTransform(new Vector2(iconRelativeWidth, 0.9f), mainGroup.RectTransform), itemIcon, scaleToFit: true)
{
CanBeFocused = false,
Color = (itemIcon == pi.ItemPrefab.InventoryIcon ? pi.ItemPrefab.InventoryIconColor : pi.ItemPrefab.SpriteColor) * (forceDisable ? 0.5f : 1.0f),
UserData = "icon"
};
@@ -748,8 +772,8 @@ namespace Barotrauma
GUILayoutGroup nameAndQuantityGroup = new GUILayoutGroup(new RectTransform(new Vector2(nameAndIconRelativeWidth - iconRelativeWidth, 1.0f), mainGroup.RectTransform))
{
Stretch = true,
ToolTip = pi.ItemPrefab.Description
CanBeFocused = false,
Stretch = true
};
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndQuantityGroup.RectTransform),
pi.ItemPrefab.Name, font: GUI.SubHeadingFont, textAlignment: Alignment.BottomLeft)
@@ -801,8 +825,8 @@ namespace Barotrauma
var priceBlock = new GUITextBlock(new RectTransform(new Vector2(priceAndButtonRelativeWidth - buttonRelativeWidth, 1.0f), mainGroup.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
{
CanBeFocused = false,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
ToolTip = pi.ItemPrefab.Description,
UserData = "price"
};
if(listBox == storeSellList || listBox == shoppingCrateSellList)

View File

@@ -788,17 +788,12 @@ namespace Barotrauma
string msg = ChatMessage.GetTimeStamp() + message.TextWithSender;
storedMessages.Add(new Pair<string, PlayerConnectionChangeType>(msg, message.ChangeType));
if (GameSession.IsTabMenuOpen)
if (GameSession.IsTabMenuOpen && selectedTab == InfoFrameTab.Crew)
{
TabMenu instance = GameSession.TabMenuInstance;
instance.AddLineToLog(msg, message.ChangeType);
// Update crew
if (selectedTab == InfoFrameTab.Crew)
{
instance.RemoveCurrentElements();
instance.CreateMultiPlayerList(true);
}
instance.RemoveCurrentElements();
instance.CreateMultiPlayerList(true);
}
}
@@ -835,14 +830,15 @@ namespace Barotrauma
break;
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), logList.Content.RectTransform), line, wrap: true, font: GUI.SmallFont)
if (logList != null)
{
TextColor = textColor,
CanBeFocused = false,
UserData = line
}.CalculateHeightFromText();
//if ((prevSize == 1.0f && listBox.BarScroll == 0.0f) || (prevSize < 1.0f && listBox.BarScroll == 1.0f)) listBox.BarScroll = 1.0f;
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), logList.Content.RectTransform), line, wrap: true, font: GUI.SmallFont)
{
TextColor = textColor,
CanBeFocused = false,
UserData = line
}.CalculateHeightFromText();
}
}
private void CreateMissionInfo(GUIFrame infoFrame)

View File

@@ -56,11 +56,6 @@ namespace Barotrauma
public static Thread MainThread { get; private set; }
public static IEnumerable<ContentPackage> SelectedPackages
{
get { return Config?.SelectedContentPackages; }
}
private static ContentPackage vanillaContent;
public static ContentPackage VanillaContent
{
@@ -69,7 +64,7 @@ namespace Barotrauma
if (vanillaContent == null)
{
// TODO: Dynamic method for defining and finding the vanilla content package.
vanillaContent = ContentPackage.List.SingleOrDefault(cp => Path.GetFileName(cp.Path).Equals("vanilla 0.9.xml", StringComparison.OrdinalIgnoreCase));
vanillaContent = ContentPackage.CorePackages.SingleOrDefault(cp => Path.GetFileName(cp.Path).Equals("vanilla 0.9.xml", StringComparison.OrdinalIgnoreCase));
}
return vanillaContent;
}
@@ -454,40 +449,31 @@ namespace Barotrauma
}
}
GUI.Init(Window, Config.SelectedContentPackages, GraphicsDevice);
GUI.Init(Window, Config.AllEnabledPackages, GraphicsDevice);
DebugConsole.Init();
if (Config.AutoUpdateWorkshopItems)
{
bool waitingForWorkshopUpdates = true;
bool result = false;
Config.WaitingForAutoUpdate = true;
TaskPool.Add("AutoUpdateWorkshopItemsAsync",
SteamManager.AutoUpdateWorkshopItemsAsync(), (task) =>
{
result = ((Task<bool>)task).Result;
waitingForWorkshopUpdates = false;
bool result = ((Task<bool>)task).Result;
Config.WaitingForAutoUpdate = false;
});
while (waitingForWorkshopUpdates) { yield return CoroutineStatus.Running; }
if (result)
{
CrossThread.RequestExecutionOnMainThread(() =>
{
ContentPackage.LoadAll();
Config.ReloadContentPackages();
});
}
while (Config.WaitingForAutoUpdate) { yield return CoroutineStatus.Running; }
}
if (SelectedPackages.None())
if (Config.AllEnabledPackages.None())
{
DebugConsole.Log("No content packages selected");
}
else
{
DebugConsole.Log("Selected content packages: " + string.Join(", ", SelectedPackages.Select(cp => cp.Name)));
DebugConsole.Log("Selected content packages: " + string.Join(", ", Config.AllEnabledPackages.Select(cp => cp.Name)));
}
#if DEBUG
@@ -542,6 +528,7 @@ namespace Barotrauma
OutpostGenerationParams.LoadPresets();
WreckAIConfig.LoadAll();
EventSet.LoadPrefabs();
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
Order.Init();
@@ -550,10 +537,6 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
StructurePrefab.LoadAll(GetFilesOfType(ContentType.Structure));
TitleScreen.LoadState = 53.0f;
yield return CoroutineStatus.Running;
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
TitleScreen.LoadState = 55.0f;
yield return CoroutineStatus.Running;
@@ -628,8 +611,6 @@ namespace Barotrauma
LocationType.Init();
MainMenuScreen.Select();
CheckContentPackage();
foreach (string steamError in SteamManager.InitializationErrors)
{
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get(steamError));
@@ -645,29 +626,6 @@ namespace Barotrauma
}
private void CheckContentPackage()
{
foreach (ContentPackage contentPackage in Config.SelectedContentPackages)
{
var exePaths = contentPackage.GetFilesOfType(ContentType.Executable);
if (exePaths.Any() && AppDomain.CurrentDomain.FriendlyName != Path.GetFileNameWithoutExtension(exePaths.First()))
{
var msgBox = new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("IncorrectExe",
new string[2] { "[selectedpackage]", "[exename]" }, new string[2] { contentPackage.Name, exePaths.First() }),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
msgBox.Buttons[0].OnClicked += (_, userdata) =>
{
string fullPath = Path.GetFullPath(exePaths.First());
ToolBox.OpenFileWithShell(fullPath);
Exit();
return true;
};
msgBox.Buttons[1].OnClicked = msgBox.Close;
break;
}
}
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
@@ -690,11 +648,11 @@ namespace Barotrauma
{
if (searchAllContentPackages)
{
return ContentPackage.GetFilesOfType(ContentPackage.List, type);
return ContentPackage.GetFilesOfType(ContentPackage.AllPackages, type);
}
else
{
return ContentPackage.GetFilesOfType(SelectedPackages, type);
return ContentPackage.GetFilesOfType(Config.AllEnabledPackages, type);
}
}
@@ -938,6 +896,7 @@ namespace Barotrauma
if (GameSession?.GameMode != null && GameSession.GameMode.Paused)
{
Paused = true;
GameSession.GameMode.UpdateWhilePaused((float)Timing.Step);
}
#if !DEBUG
@@ -960,7 +919,6 @@ namespace Barotrauma
DebugConsole.AddToGUIUpdateList();
DebugConsole.Update((float)Timing.Step);
Paused = Paused || (DebugConsole.IsOpen && (NetworkMember == null || !NetworkMember.GameStarted));
if (!Paused)
{
@@ -1088,11 +1046,11 @@ namespace Barotrauma
}
// Update store stock when saving and quitting in an outpost (normally updated when CampaignMode.End() is called)
if (GameSession?.Campaign is SinglePlayerCampaign campaign && Level.IsLoadedOutpost && campaign.Map?.CurrentLocation != null && campaign.CargoManager != null)
if (GameSession?.Campaign is SinglePlayerCampaign spCampaign && Level.IsLoadedOutpost && spCampaign.Map?.CurrentLocation != null && spCampaign.CargoManager != null)
{
campaign.Map.CurrentLocation.AddToStock(campaign.CargoManager.SoldItems);
campaign.CargoManager.ClearSoldItemsProjSpecific();
campaign.Map.CurrentLocation.RemoveFromStock(campaign.CargoManager.PurchasedItems);
spCampaign.Map.CurrentLocation.AddToStock(spCampaign.CargoManager.SoldItems);
spCampaign.CargoManager.ClearSoldItemsProjSpecific();
spCampaign.Map.CurrentLocation.RemoveFromStock(spCampaign.CargoManager.PurchasedItems);
}
SaveUtil.SaveGame(GameSession.SavePath);

View File

@@ -10,8 +10,17 @@ namespace Barotrauma
{
public enum SellStatus
{
/// <summary>
/// Entity sold in SP. Or, entity sold by client and confirmed by server in MP.
/// </summary>
Confirmed,
/// <summary>
/// Entity sold by client in MP. Client has received at least one update from server after selling, but this entity wasn't yet confirmed.
/// </summary>
Unconfirmed,
/// <summary>
/// Entity sold by client in MP. Client hasn't yet received an update from server after selling.
/// </summary>
Local
}
@@ -36,28 +45,41 @@ namespace Barotrauma
// Only consider items which have been:
// a) sold in singleplayer or confirmed by server (SellStatus.Confirmed); or
// b) sold locally in multiplier (SellStatus.Local), but the client has not received a campaing state update yet after selling them
// b) sold locally in multiplayer (SellStatus.Local), but the client has not received a campaing state update yet after selling them
var soldEntities = SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
var sellables = Item.ItemList.FindAll(i => i?.Prefab != null && !i.Removed &&
i.GetRootInventoryOwner() == character &&
!i.SpawnedInOutpost &&
(i.ContainedItems == null || i.ContainedItems.None() || i.ContainedItems.All(ci => soldEntities.Any(se => se.Item == ci))) &&
i.IsFullCondition && soldEntities.None(se => se.Item == i));
i.Condition >= 0.9f * i.MaxCondition && soldEntities.None(se => se.Item == i));
// Prevent selling things like battery cells from headsets and oxygen tanks from diving masks
var slots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.OuterClothes, InvSlotType.Headset };
// Prevent selling items in equipment slots
var slots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card };
foreach (InvSlotType slot in slots)
{
var index = character.Inventory.FindLimbSlot(slot);
if (character.Inventory.Items[index] is Item item && item.ContainedItems != null)
if (character.Inventory.Items[index] is Item item)
{
// Don't prevent selling of items which can only be put in equipment slots (like diving suits)
if (item.AllowedSlots.Contains(InvSlotType.Any))
{
sellables.Remove(item);
}
}
}
// Prevent selling items contained in certain equipped items (like battery cell in equipped headset or oxygen tank in equipped diving mask)
slots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.OuterClothes, InvSlotType.Headset };
foreach (InvSlotType slot in slots)
{
var index = character.Inventory.FindLimbSlot(slot);
if (character.Inventory.Items[index] is Item item &&
item.ContainedItems != null && item.AllowedSlots.Contains(InvSlotType.Any))
{
foreach (Item containedItem in item.ContainedItems)
{
if (containedItem != null)
{
sellables.Remove(containedItem);
}
sellables.Remove(containedItem);
}
}
}
@@ -123,7 +145,7 @@ namespace Barotrauma
var itemValue = GetSellValueAtCurrentLocation(item.ItemPrefab, quantity: item.Quantity);
// check if the store can afford the item
if (location.StoreCurrentBalance < itemValue) { continue; }
if (Location.StoreCurrentBalance < itemValue) { continue; }
var matchingItems = itemsInInventory.FindAll(i => i.Prefab == item.ItemPrefab);
if (matchingItems.Count <= item.Quantity)
@@ -147,7 +169,7 @@ namespace Barotrauma
}
// Exchange money
campaign.Map.CurrentLocation.StoreCurrentBalance -= itemValue;
Location.StoreCurrentBalance -= itemValue;
campaign.Money += itemValue;
// Remove from the sell crate

View File

@@ -15,11 +15,6 @@ namespace Barotrauma
{
partial class CrewManager
{
/// <summary>
/// How long the previously selected character waits doing nothing when switching to another character. Only affects idling.
/// </summary>
const float CharacterWaitOnSwitch = 10.0f;
private Point screenResolution;
#region UI
@@ -63,6 +58,8 @@ namespace Barotrauma
private Sprite jobIndicatorBackground, previousOrderArrow, cancelIcon;
private const int MaxOrderIcons = 3;
#endregion
#region Constructors
@@ -363,9 +360,10 @@ namespace Barotrauma
UserData = character
};
// "Padding" to prevent member-specific command button from overlapping job indicator
var commandButtonAbsoluteHeight = Math.Min(40.0f, 0.67f * background.Rect.Height);
var paddingRelativeWidth = 0.35f * commandButtonAbsoluteHeight / background.Rect.Width;
// "Padding" to prevent member-specific command button from overlapping job indicator
new GUIFrame(new RectTransform(new Vector2(paddingRelativeWidth, 1.0f), layoutGroup.RectTransform), style: null);
var jobIconBackground = new GUIImage(
@@ -392,7 +390,16 @@ namespace Barotrauma
};
}
var nameRelativeWidth = 1.0f - paddingRelativeWidth - 3.7f * iconRelativeWidth;
var nameRelativeWidth = 1.0f
// Start padding
- paddingRelativeWidth
// 5 icons (job, 3 orders, sound)
- (5 * 0.8f * iconRelativeWidth)
// Vertical line
- (0.1f * iconRelativeWidth)
// Spacing
- (7 * layoutGroup.RelativeSpacing);
var font = layoutGroup.Rect.Width < 150 ? GUI.SmallFont : GUI.Font;
var nameBlock = new GUITextBlock(
new RectTransform(
@@ -417,6 +424,7 @@ namespace Barotrauma
{
UserData = character
};
// Only create a tooltip if the name doesn't fit the name block
if (nameBlock.Text.EndsWith("..."))
{
@@ -432,6 +440,7 @@ namespace Barotrauma
}};
}
}
if (IsSinglePlayer)
{
characterButton.OnClicked = CharacterClicked;
@@ -443,7 +452,7 @@ namespace Barotrauma
}
new GUIImage(
new RectTransform(new Vector2(0.5f * iconRelativeWidth, 0.5f), layoutGroup.RectTransform),
new RectTransform(new Vector2(0.1f * iconRelativeWidth, 0.5f), layoutGroup.RectTransform),
style: "VerticalLine")
{
CanBeFocused = false
@@ -697,7 +706,7 @@ namespace Barotrauma
/// <summary>
/// Displays the specified order in the crew UI next to the character.
/// </summary>
public void DisplayCharacterOrder(Character character, Order order, string option)
public void AddCurrentOrderIcon(Character character, Order order, string option)
{
if (character == null) { return; }
@@ -706,34 +715,44 @@ namespace Barotrauma
if (characterFrame == null) { return; }
GUILayoutGroup layoutGroup = (GUILayoutGroup)characterFrame.FindChild(c => c is GUILayoutGroup);
var currentOrderComponent = GetCurrentOrderComponent(layoutGroup);
if (order != null)
// Get the OrderInfo from the current order icon
var currentOrderIcon = GetCurrentOrderIcon(layoutGroup);
OrderInfo? currentOrderInfo = null;
if (currentOrderIcon?.UserData is OrderInfo)
{
var prevOrderComponent = GetPreviousOrderComponent(layoutGroup);
if (currentOrderComponent?.UserData is OrderInfo currentOrderInfo)
{
if (order.Identifier == currentOrderInfo.Order.Identifier &&
option == currentOrderInfo.OrderOption &&
order.TargetEntity == currentOrderInfo.Order.TargetEntity) { return; }
currentOrderInfo = (OrderInfo)currentOrderIcon.UserData;
// No need to recreate icons if the current order matches the new order
if (currentOrderInfo.Value.MatchesOrder(order, option)) { return; }
}
layoutGroup.RemoveChild(prevOrderComponent);
DisplayPreviousCharacterOrder(character, layoutGroup, currentOrderInfo);
}
else if (order.Identifier != dismissedOrderPrefab.Identifier &&
prevOrderComponent?.UserData is OrderInfo prevOrderInfo &&
order.Identifier == prevOrderInfo.Order.Identifier &&
option == prevOrderInfo.OrderOption &&
order.TargetEntity == prevOrderInfo.Order.TargetEntity)
// Remove the current order icon
layoutGroup.RemoveChild(currentOrderIcon);
// Remove a previous order icon if it matches the new order
// We don't want the same order as both a current order and a previous order
foreach (GUIComponent icon in GetPreviousOrderIcons(layoutGroup))
{
if (icon?.UserData is OrderInfo info && info.MatchesOrder(order, option))
{
layoutGroup.RemoveChild(prevOrderComponent);
layoutGroup.RemoveChild(icon);
break;
}
}
layoutGroup.RemoveChild(currentOrderComponent);
// Create a new previous order icon from the current order icon's OrderInfo
if (currentOrderInfo.HasValue)
{
AddPreviousOrderIcon(character, layoutGroup, currentOrderInfo.Value);
}
if (order == null || order.Identifier == dismissedOrderPrefab.Identifier) { return; }
if (GetPreviousOrderIcons(layoutGroup).Count() >= MaxOrderIcons)
{
RemoveLastPreviousOrderIcon(layoutGroup);
}
var orderFrame = new GUIButton(
new RectTransform(
layoutGroup.GetChildByUserData("job").RectTransform.RelativeSize,
@@ -748,25 +767,31 @@ namespace Barotrauma
return true;
}
};
CreateNodeIcon(orderFrame.RectTransform, order.SymbolSprite, order.Color, tooltip: order.Name);
new GUIImage(
new RectTransform(Vector2.One, orderFrame.RectTransform),
cancelIcon,
scaleToFit: true)
new GUIImage(new RectTransform(Vector2.One, orderFrame.RectTransform), cancelIcon, scaleToFit: true)
{
CanBeFocused = false,
UserData = "cancel",
Visible = false
};
orderFrame.RectTransform.RepositionChildInHierarchy(4);
characterFrame.SetAsFirstChild();
}
private void DisplayPreviousCharacterOrder(Character character, GUILayoutGroup characterComponent, OrderInfo orderInfo)
private void AddPreviousOrderIcon(Character character, GUILayoutGroup characterComponent, OrderInfo orderInfo)
{
if (orderInfo.Order == null || orderInfo.Order.Identifier == dismissedOrderPrefab.Identifier) { return; }
var maxPreviousOrderIcons = GetCurrentOrderIcon(characterComponent) != null ? MaxOrderIcons - 1 : MaxOrderIcons;
if (GetPreviousOrderIcons(characterComponent).Count() >= maxPreviousOrderIcons)
{
RemoveLastPreviousOrderIcon(characterComponent);
}
var previousOrderInfo = new OrderInfo(orderInfo);
var prevOrderFrame = new GUIButton(
new RectTransform(
characterComponent.GetChildByUserData("job").RectTransform.RelativeSize,
@@ -786,17 +811,20 @@ namespace Barotrauma
var prevOrderIconFrame = new GUIFrame(
new RectTransform(new Vector2(0.8f), prevOrderFrame.RectTransform, anchor: Anchor.BottomLeft),
style: null);
CreateNodeIcon(
prevOrderIconFrame.RectTransform,
previousOrderInfo.Order.SymbolSprite,
previousOrderInfo.Order.Color,
tooltip: previousOrderInfo.Order.Name);
foreach (GUIComponent c in prevOrderIconFrame.Children)
{
c.HoverColor = c.Color;
c.PressedColor = c.Color;
c.SelectedColor = c.Color;
}
new GUIImage(
new RectTransform(new Vector2(0.8f), prevOrderFrame.RectTransform, anchor: Anchor.TopRight),
previousOrderArrow,
@@ -804,19 +832,63 @@ namespace Barotrauma
{
CanBeFocused = false
};
prevOrderFrame.RectTransform.RepositionChildInHierarchy(GetCurrentOrderComponent(characterComponent) != null ? 5 : 4);
var positionInHierarchy = GetCurrentOrderIcon(characterComponent) != null ? 5 : 4;
prevOrderFrame.RectTransform.RepositionChildInHierarchy(positionInHierarchy);
}
private GUIComponent GetCurrentOrderComponent(GUILayoutGroup characterComponent)
private void AddOldPreviousOrderIcons(Character character, GUILayoutGroup oldCharacterComponent)
{
return characterComponent?.FindChild(c => c?.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "currentorder");
var prevOrderIcons = GetPreviousOrderIcons(oldCharacterComponent).ToList();
if (prevOrderIcons.None()) { return; }
if (prevOrderIcons.Count() > 1)
{
prevOrderIcons.Sort((x, y) => oldCharacterComponent.GetChildIndex(x).CompareTo(oldCharacterComponent.GetChildIndex(y)));
prevOrderIcons.Reverse();
}
if (crewList.Content.Children.FirstOrDefault(c => c?.UserData == character)?.GetChild<GUILayoutGroup>() is GUILayoutGroup newCharacterComponent)
{
foreach (GUIComponent icon in prevOrderIcons)
{
if (icon.UserData is OrderInfo orderInfo)
{
AddPreviousOrderIcon(character, newCharacterComponent, orderInfo);
}
}
}
}
private GUIComponent GetPreviousOrderComponent(GUILayoutGroup characterComponent)
private void RemoveLastPreviousOrderIcon(GUILayoutGroup characterComponent)
{
return characterComponent?.FindChild(c => c?.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "previousorder");
var prevOrderIcons = GetPreviousOrderIcons(characterComponent);
if (prevOrderIcons.None()) { return; }
if (prevOrderIcons.Count() == 1)
{
characterComponent.RemoveChild(prevOrderIcons.First());
}
else
{
int highestIndex = 0;
GUIComponent oldestPreviousOrderIcon = null;
foreach (GUIComponent icon in prevOrderIcons)
{
int i = characterComponent.GetChildIndex(icon);
if (i > highestIndex || oldestPreviousOrderIcon == null)
{
highestIndex = i;
oldestPreviousOrderIcon = icon;
}
}
characterComponent.RemoveChild(oldestPreviousOrderIcon);
}
}
private GUIComponent GetCurrentOrderIcon(GUILayoutGroup characterComponent) =>
characterComponent?.FindChild(c => c?.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "currentorder");
private IEnumerable<GUIComponent> GetPreviousOrderIcons(GUILayoutGroup characterComponent) =>
characterComponent?.FindChildren(c => c?.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "previousorder");
#endregion
#region Updating and drawing the UI
@@ -854,7 +926,7 @@ namespace Barotrauma
{
if (IsSinglePlayer || client == null || (GameMain.NetworkMember?.ConnectedClients?.All(match => match != client) ?? true)) { return; }
contextMenu = new GUIFrame(new RectTransform(new Vector2(0.1f, 0.12f), GUI.Canvas) { ScreenSpaceOffset = mousePos }, style: "GUIToolTip") { UserData = client };
contextMenu = new GUIFrame(new RectTransform(new Vector2(0.1f, 0.15f), GUI.Canvas) { ScreenSpaceOffset = mousePos }, style: "GUIToolTip") { UserData = client };
var nameLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.2f), contextMenu.RectTransform), client.Name, font: GUI.SubHeadingFont)
{
@@ -864,7 +936,9 @@ namespace Barotrauma
var optionsList = new GUIListBox(new RectTransform(new Vector2(1f, 0.8f), contextMenu.RectTransform, Anchor.BottomLeft), style: null)
{
Padding = new Vector4(4, 0, 4, 4)
Padding = new Vector4(4, 0, 4, 4),
AutoHideScrollBar = false,
ScrollBarVisible = false
};
bool hasSteam = client.SteamID > 0 && SteamManager.IsInitialized,
@@ -886,6 +960,13 @@ namespace Barotrauma
UserData = "steam"
};
new GUITextBlock(new RectTransform(Point.Zero, parent), TextManager.Get("moderationmenu.userdetails"), font: GUI.SmallFont)
{
Padding = new Vector4(4),
Enabled = true,
UserData = "user"
};
new GUITextBlock(new RectTransform(Point.Zero, parent), TextManager.Get("permissions"), font: GUI.SmallFont)
{
Padding = new Vector4(4),
@@ -971,6 +1052,9 @@ namespace Barotrauma
case "ban":
GameMain.Client?.CreateKickReasonPrompt(client.Name, true);
break;
case "user":
GameMain.NetLobbyScreen?.SelectPlayer(client);
break;
}
contextMenu = null;
return true;
@@ -983,7 +1067,11 @@ namespace Barotrauma
{
if (client == null ) { return; }
subContextMenu = new GUIListBox(new RectTransform(new Vector2(0.1f, 0.1f), GUI.Canvas) { ScreenSpaceOffset = pos }, style: "GUIToolTip");
subContextMenu = new GUIListBox(new RectTransform(new Vector2(0.1f, 0.1f), GUI.Canvas) { ScreenSpaceOffset = pos }, style: "GUIToolTip")
{
AutoHideScrollBar = false,
ScrollBarVisible = false
};
foreach (var rank in PermissionPreset.List)
{
@@ -1073,11 +1161,7 @@ namespace Barotrauma
{
if (!(c.UserData is Character character) || character.IsDead || character.Removed) { continue; }
AddCharacter(character);
if (GetPreviousOrderComponent(c.GetChild<GUILayoutGroup>())?.UserData is OrderInfo prevInfo &&
crewList.Content.Children.FirstOrDefault(c => c?.UserData == character)?.GetChild<GUILayoutGroup>() is GUILayoutGroup newLayoutGroup)
{
DisplayPreviousCharacterOrder(character, newLayoutGroup, prevInfo);
}
AddOldPreviousOrderIcons(character, c.GetChild<GUILayoutGroup>());
}
}
@@ -1112,9 +1196,10 @@ namespace Barotrauma
if (!AllowCharacterSwitch) { return; }
//make the previously selected character wait in place for some time
//(so they don't immediately start idling and walking away from their station)
if (Character.Controlled?.AIController?.ObjectiveManager != null)
var aiController = Character.Controlled?.AIController;
if (aiController != null)
{
Character.Controlled.AIController.ObjectiveManager.WaitTimer = CharacterWaitOnSwitch;
aiController.Reset();
}
DisableCommandUI();
Character.Controlled = character;
@@ -1419,7 +1504,7 @@ namespace Barotrauma
}
if (child.FindChild(c => c is GUILayoutGroup) is GUILayoutGroup layoutGroup)
{
if (GetCurrentOrderComponent(layoutGroup) is GUIComponent orderButton &&
if (GetCurrentOrderIcon(layoutGroup) is GUIComponent orderButton &&
orderButton.GetChildByUserData("colorsource") is GUIComponent orderIcon &&
orderButton.GetChildByUserData("cancel") is GUIComponent cancelIcon)
{
@@ -1504,8 +1589,8 @@ namespace Barotrauma
private Hull hullContext;
private bool isContextual;
private readonly List<Order> contextualOrders = new List<Order>();
private Point shorcutCenterNodeOffset;
private const int maxShorcutNodeCount = 4;
private Point shortcutCenterNodeOffset;
private const int maxShortcutNodeCount = 4;
private bool WasCommandInterfaceDisabledThisUpdate { get; set; }
private bool CanIssueOrders
@@ -1688,7 +1773,7 @@ namespace Barotrauma
returnNodeMargin = returnNodeSize.X * 0.5f;
nodeDistance = (int)(150 * GUI.Scale);
shorcutCenterNodeOffset = new Point(0, (int)(1.25f * nodeDistance));
shortcutCenterNodeOffset = new Point(0, (int)(1.25f * nodeDistance));
}
private List<OrderCategory> GetAvailableCategories()
@@ -1972,7 +2057,7 @@ namespace Barotrauma
shortcutNodes.Clear();
if (shortcutNodes.Count < maxShorcutNodeCount && sub.GetItems(false).Find(i => i.HasTag("reactor") && !i.NonInteractable)?.GetComponent<Reactor>() is Reactor reactor)
if (shortcutNodes.Count < maxShortcutNodeCount && sub.GetItems(false).Find(i => i.HasTag("reactor") && !i.NonInteractable)?.GetComponent<Reactor>() is Reactor reactor)
{
var reactorOutput = -reactor.CurrPowerConsumption;
// If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor
@@ -1990,7 +2075,7 @@ namespace Barotrauma
// TODO: Reconsider the conditions as bot captain can have the nav term selected without operating it
// If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up
// --> Create shortcut node for Steer order
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("captain")) &&
if (shortcutNodes.Count < maxShortcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("captain")) &&
sub.GetItems(false).Find(i => i.HasTag("navterminal") && !i.NonInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) &&
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
{
@@ -2000,7 +2085,7 @@ namespace Barotrauma
// If player is not a security officer AND invaders are reported
// --> Create shorcut node for Fight Intruders order
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("securityofficer")) &&
if (shortcutNodes.Count < maxShortcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("securityofficer")) &&
(Order.GetPrefab("reportintruders") is Order reportIntruders && ActiveOrders.Any(o => o.First.Prefab == reportIntruders)))
{
shortcutNodes.Add(
@@ -2009,7 +2094,7 @@ namespace Barotrauma
// If player is not a mechanic AND a breach has been reported
// --> Create shorcut node for Fix Leaks order
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("mechanic")) &&
if (shortcutNodes.Count < maxShortcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("mechanic")) &&
(Order.GetPrefab("reportbreach") is Order reportBreach && ActiveOrders.Any(o => o.First.Prefab == reportBreach)))
{
shortcutNodes.Add(
@@ -2018,7 +2103,7 @@ namespace Barotrauma
// If player is not an engineer AND broken devices have been reported
// --> Create shortcut node for Repair Damaged Systems order
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("engineer")) &&
if (shortcutNodes.Count < maxShortcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("engineer")) &&
(Order.GetPrefab("reportbrokendevices") is Order reportBrokenDevices && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices)))
{
shortcutNodes.Add(
@@ -2027,13 +2112,13 @@ namespace Barotrauma
// If fire is reported
// --> Create shortcut node for Extinguish Fires order
if (shortcutNodes.Count < maxShorcutNodeCount && ActiveOrders.Any(o=> o.First.Prefab == Order.GetPrefab("reportfire")))
if (shortcutNodes.Count < maxShortcutNodeCount && ActiveOrders.Any(o=> o.First.Prefab == Order.GetPrefab("reportfire")))
{
shortcutNodes.Add(
CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab("extinguishfires"), -1));
}
if (shortcutNodes.Count < maxShorcutNodeCount && characterContext?.Info?.Job?.Prefab?.AppropriateOrders != null)
if (shortcutNodes.Count < maxShortcutNodeCount && characterContext?.Info?.Job?.Prefab?.AppropriateOrders != null)
{
foreach (string orderIdentifier in characterContext.Info.Job.Prefab.AppropriateOrders)
{
@@ -2046,7 +2131,7 @@ namespace Barotrauma
{
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, orderPrefab, -1));
}
if (shortcutNodes.Count >= maxShorcutNodeCount) { break; }
if (shortcutNodes.Count >= maxShortcutNodeCount) { break; }
}
}
}
@@ -2063,7 +2148,7 @@ namespace Barotrauma
c.PressedColor = c.Color;
c.SelectedColor = c.Color;
}
shortcutCenterNode.RectTransform.MoveOverTime(shorcutCenterNodeOffset, CommandNodeAnimDuration);
shortcutCenterNode.RectTransform.MoveOverTime(shortcutCenterNodeOffset, CommandNodeAnimDuration);
var nodeCountForCalculations = shortcutNodes.Count * 2 + 2;
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, 0.75f * nodeDistance, nodeCountForCalculations);
@@ -2071,7 +2156,7 @@ namespace Barotrauma
for (int i = 0; i < shortcutNodes.Count; i++)
{
shortcutNodes[i].RectTransform.Parent = commandFrame.RectTransform;
shortcutNodes[i].RectTransform.MoveOverTime(shorcutCenterNodeOffset + offsets[firstOffsetIndex - i].ToPoint(), CommandNodeAnimDuration);
shortcutNodes[i].RectTransform.MoveOverTime(shortcutCenterNodeOffset + offsets[firstOffsetIndex - i].ToPoint(), CommandNodeAnimDuration);
}
}
@@ -2100,6 +2185,8 @@ namespace Barotrauma
{
if (contextualOrders.None())
{
string orderIdentifier;
// Check if targeting an item or a hull
if (itemContext != null && !itemContext.NonInteractable)
{
@@ -2114,7 +2201,7 @@ namespace Barotrauma
}
// If targeting a periscope connected to a turret, show the 'operateweapons' order
var orderIdentifier = "operateweapons";
orderIdentifier = "operateweapons";
var operateWeaponsPrefab = Order.GetPrefab(orderIdentifier);
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Components.Any(c => c is Controller))
{
@@ -2123,9 +2210,9 @@ namespace Barotrauma
if (turret != null) { contextualOrders.Add(new Order(operateWeaponsPrefab, turret.Item, turret, Character.Controlled)); }
}
// If targeting a repairable item, show the 'repairsystems' order
// If targeting a repairable item with condition below the repair threshold, show the 'repairsystems' order
orderIdentifier = "repairsystems";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Repairables.Any())
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Repairables.Any(r => itemContext.ConditionPercentage < r.RepairThreshold))
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, null, Character.Controlled));
if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical"))))
@@ -2138,13 +2225,6 @@ namespace Barotrauma
}
}
// Always show the 'wait' order if there are other crew members alive
orderIdentifier = "wait";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && characters.Any(c => c != Character.Controlled))
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, null, Character.Controlled));
}
// Remove the 'pumpwater' order if the target pump is auto-controlled (as it will immediately overwrite the work done by the bot)
orderIdentifier = "pumpwater";
if (contextualOrders.FirstOrDefault(o => o.Identifier.Equals(orderIdentifier)) is Order o &&
@@ -2152,20 +2232,34 @@ namespace Barotrauma
{
if (pump.IsAutoControlled) { contextualOrders.Remove(o); }
}
if (contextualOrders.None())
{
// If there are other crew members alive and there are no other contextual orders available, show the 'wait' order
orderIdentifier = "wait";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && characters.Any(c => c != Character.Controlled))
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, null, Character.Controlled));
}
}
}
else if(hullContext != null)
{
contextualOrders.Add(new Order(Order.GetPrefab("fixleaks"), hullContext, null, Character.Controlled));
}
// Show the 'follow' and 'dismissed' orders if there are other crew members alive
if (characters.Any(c => c != Character.Controlled))
{
var orderIdentifier = "follow";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
// Show 'follow' order only when there are no orders of other categories available
if (contextualOrders.None(o => o.Category != OrderCategory.Movement))
{
contextualOrders.Add(Order.GetPrefab(orderIdentifier));
orderIdentifier = "follow";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{
contextualOrders.Add(Order.GetPrefab(orderIdentifier));
}
}
// Show 'dismissed' order only when there are crew members with active orders
orderIdentifier = "dismissed";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) &&
@@ -2192,7 +2286,7 @@ namespace Barotrauma
if (Order.PrefabList.Any(o => item.HasTag(o.ItemIdentifiers))) { return true; }
if (Order.PrefabList.Any(o => o.ItemComponentType != null && item.Components.Any(c => c?.GetType() == o.ItemComponentType))) { return true; }
if (item.Repairables.Any()) { return true; }
if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold)) { return true; }
var operateWeaponsPrefab = Order.GetPrefab("operateweapons");
return item.Components.Any(c => c is Controller) &&
(item.GetConnectedComponents<Turret>().Any(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier)) ||
@@ -2209,8 +2303,30 @@ namespace Barotrauma
node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration);
if (checkIfOrderCanBeHeard && !disableNode) { disableNode = !CanSomeoneHearCharacter(); }
var mustSetOptionOrTarget = order.HasOptions || (order.MustSetTarget && itemContext == null);
if (checkIfOrderCanBeHeard && !disableNode)
{
disableNode = !CanSomeoneHearCharacter();
}
var mustSetOptionOrTarget = order.HasOptions;
Item orderTargetEntity = null;
// If the order doesn't have options, but must set a target,
// we have to check if there's only one possible target available
// so we know to directly target that with the order
if (!mustSetOptionOrTarget && order.MustSetTarget && itemContext == null)
{
var matchingItems = order.GetMatchingItems(GetTargetSubmarine(), true);
if (matchingItems.Count > 1)
{
mustSetOptionOrTarget = true;
}
else
{
orderTargetEntity = matchingItems.FirstOrDefault();
}
}
node.OnClicked = (button, userData) =>
{
if (disableNode || !CanIssueOrders) { return false; }
@@ -2224,7 +2340,11 @@ namespace Barotrauma
NavigateForward(button, userData);
}
else
{
{
if (orderTargetEntity != null)
{
o = new Order(o.Prefab, orderTargetEntity, orderTargetEntity.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: order.OrderGiver);
}
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o), o, null, Character.Controlled);
DisableCommandUI();
}
@@ -2382,6 +2502,10 @@ namespace Barotrauma
Sprite icon = null;
order.MinimapIcons?.TryGetValue(item.Prefab.Identifier, out icon);
if (item.Prefab.MinimapIcon != null)
{
icon = item.Prefab.MinimapIcon;
}
var colorMultiplier = characters.Any(c => c.CurrentOrder != null &&
c.CurrentOrder.Identifier == userData.Item1.Identifier &&
c.CurrentOrder.TargetEntity == userData.Item1.TargetEntity) ? 0.5f : 1f;
@@ -2601,7 +2725,7 @@ namespace Barotrauma
var availableNodePositions = 20;
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, 2.7f * this.nodeDistance, availableNodePositions,
firstAngle: MathHelper.ToRadians(-90f - ((extraOptionCharacters.Count - 1) * 0.5f * (360f / availableNodePositions))));
for (int i = 0; i < extraOptionCharacters.Count; i++)
for (int i = 0; i < extraOptionCharacters.Count && i < availableNodePositions; i++)
{
CreateAssignmentNode(userData as Tuple<Order, string>, extraOptionCharacters[i], offsets[i].ToPoint(), -1, nameLabelScale: 1.15f);
}
@@ -2786,7 +2910,7 @@ namespace Barotrauma
{
bearing = GetBearing(
centerNode.RectTransform.AnimTargetPos.ToVector2(),
shorcutCenterNodeOffset.ToVector2());
shortcutCenterNodeOffset.ToVector2());
}
return nodeCount % 2 > 0 ?
MathHelper.ToRadians(bearing + 360.0f / nodeCount / 2) :

View File

@@ -1,4 +1,5 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -30,11 +31,6 @@ namespace Barotrauma
protected set;
}
public override bool Paused
{
get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"); }
}
private bool showCampaignUI;
private bool wasChatBoxOpen;
public bool ShowCampaignUI
@@ -165,7 +161,6 @@ namespace Barotrauma
}
break;
case TransitionType.LeaveLocation:
// not sure why this can happen at an outpost but it apparently can in multiplayer
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
break;
@@ -195,14 +190,26 @@ namespace Barotrauma
if (endRoundButton.Visible)
{
if (!AllowedToEndRound()) { buttonText = TextManager.Get("map"); }
endRoundButton.Text = ToolBox.LimitString(buttonText, endRoundButton.Font, endRoundButton.Rect.Width - 5);
if (endRoundButton.Text != buttonText)
{
endRoundButton.ToolTip = buttonText;
}
endRoundButton.Enabled = AllowedToEndRound();
if (Character.Controlled?.ViewTarget is Item item)
{
Turret turret = item.GetComponent<Turret>();
endRoundButton.RectTransform.ScreenSpaceOffset = turret == null ? Point.Zero : new Point(0, (int)(turret.UIElementHeight * 1.25f));
}
else if (Character.Controlled?.CharacterHealth?.SuicideButton?.Visible ?? false)
{
endRoundButton.RectTransform.ScreenSpaceOffset = new Point(0, Character.Controlled.CharacterHealth.SuicideButton.Rect.Height);
}
else
{
endRoundButton.RectTransform.ScreenSpaceOffset = Point.Zero;
}
}
endRoundButton.DrawManually(spriteBatch);
}
@@ -216,7 +223,14 @@ namespace Barotrauma
{
await Task.Yield();
Rand.ThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror);
try
{
GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror);
}
catch (Exception e)
{
roundSummaryScreen.LoadException = e;
}
Rand.ThreadId = 0;
});
TaskPool.Add("AsyncCampaignStartRound", loadTask, (t) =>

View File

@@ -3,7 +3,7 @@ using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
@@ -13,6 +13,11 @@ namespace Barotrauma
{
public bool SuppressStateSending = false;
public override bool Paused
{
get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"); }
}
private UInt16 pendingSaveID = 1;
public UInt16 PendingSaveID
{
@@ -100,7 +105,7 @@ namespace Barotrauma
};
int buttonHeight = (int)(GUI.Scale * 40);
int buttonWidth = GUI.IntScale(200);
int buttonWidth = GUI.IntScale(450);
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUICanvas.Instance),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
@@ -209,6 +214,7 @@ namespace Barotrauma
Character.Controlled = null;
prevControlled.ClearInputs();
}
GameMain.GameScreen.Cam.Freeze = true;
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
yield return CoroutineStatus.Running;
@@ -357,7 +363,7 @@ namespace Barotrauma
base.Update(deltaTime);
if (PlayerInput.RightButtonClicked() ||
if (PlayerInput.SecondaryMouseButtonClicked() ||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
{
ShowCampaignUI = false;
@@ -403,6 +409,14 @@ namespace Barotrauma
if (CampaignUI == null) { InitCampaignUI(); }
}
else
{
var transitionType = GetAvailableTransition(out _, out _);
if (transitionType == TransitionType.None && CampaignUI?.SelectedTab == InteractionType.Map)
{
ShowCampaignUI = false;
}
}
}
public override void End(TransitionType transitionType = TransitionType.None)
{
@@ -528,6 +542,7 @@ namespace Barotrauma
UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16();
byte selectedMissionIndex = msg.ReadByte();
bool allowDebugTeleport = msg.ReadBoolean();
float? reputation = null;
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
@@ -640,6 +655,7 @@ namespace Barotrauma
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.Map.SelectMission(selectedMissionIndex);
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.CargoManager.SetPurchasedItems(purchasedItems);
campaign.CargoManager.SetSoldItems(soldItems);

View File

@@ -11,6 +11,35 @@ namespace Barotrauma
{
class SinglePlayerCampaign : CampaignMode
{
public override bool Paused
{
get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition") || ShowCampaignUI && CampaignUI.SelectedTab == InteractionType.Map; }
}
public override void UpdateWhilePaused(float deltaTime)
{
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; }
if (PlayerInput.RightButtonClicked() ||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
{
ShowCampaignUI = false;
if (GUIMessageBox.VisibleBox?.UserData is RoundSummary roundSummary &&
roundSummary.ContinueButton != null &&
roundSummary.ContinueButton.Visible)
{
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
}
}
if (CrewManager.ChatBox != null)
{
CrewManager.ChatBox.Update(deltaTime);
}
CrewManager.UpdateReports();
}
private float endTimer;
private bool savedOnStart;
@@ -124,7 +153,7 @@ namespace Barotrauma
private void InitUI()
{
int buttonHeight = (int)(GUI.Scale * 40);
int buttonWidth = GUI.IntScale(200);
int buttonWidth = GUI.IntScale(450);
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUICanvas.Instance),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
@@ -357,10 +386,11 @@ namespace Barotrauma
break;
case TransitionType.ProgressToNextLocation:
Map.MoveToNextLocation();
Map.ProgressWorld();
break;
}
Map.ProgressWorld(transitionType, (float)(Timing.TotalTime - GameMain.GameSession.RoundStartTime));
var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
transitionType == TransitionType.LeaveLocation ? Alignment.BottomCenter : Alignment.Center,
fadeOut: false,
@@ -383,6 +413,8 @@ namespace Barotrauma
//--------------------------------------
bool save = false;
if (success)
{
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
@@ -398,21 +430,33 @@ namespace Barotrauma
}
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
if (PendingSubmarineSwitch != null)
{
SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo;
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
PendingSubmarineSwitch = null;
for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++)
{
if (GameMain.GameSession.OwnedSubmarines[i].Name == previousSub.Name)
{
GameMain.GameSession.OwnedSubmarines[i] = previousSub;
break;
}
}
}
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
}
else
{
PendingSubmarineSwitch = null;
EnableRoundSummaryGameOverState();
}
//--------------------------------------
if (PendingSubmarineSwitch != null)
{
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
PendingSubmarineSwitch = null;
}
SelectSummaryScreen(roundSummary, newLevel, mirror, () =>
{
GameMain.GameScreen.Select();
@@ -473,7 +517,7 @@ namespace Barotrauma
base.Update(deltaTime);
if (PlayerInput.RightButtonClicked() ||
if (PlayerInput.SecondaryMouseButtonClicked() ||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
{
ShowCampaignUI = false;
@@ -581,7 +625,7 @@ namespace Barotrauma
}
else if (transitionType == TransitionType.ProgressToNextEmptyLocation)
{
Map.SetLocation(Map.Locations.IndexOf(Level.Loaded.EndLocation));
Map.SetLocation(Map.Locations.IndexOf(Level.Loaded.EndLocation ?? Map.CurrentLocation));
}
var subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);

View File

@@ -1,5 +1,9 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
namespace Barotrauma
{
@@ -7,6 +11,17 @@ namespace Barotrauma
{
public Action OnRoundEnd;
public bool SpawnOutpost;
public OutpostGenerationParams OutpostParams;
public LocationType OutpostType;
public EventPrefab TriggeredEvent;
private List<Event> scriptedEvent;
private GUIButton createEventButton;
public TestGameMode(GameModePreset preset) : base(preset)
{
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
@@ -24,11 +39,96 @@ namespace Barotrauma
base.Start();
CrewManager.InitSinglePlayerRound();
if (SpawnOutpost)
{
GenerateOutpost(Submarine.MainSub);
}
if (TriggeredEvent != null)
{
scriptedEvent = new List<Event> { TriggeredEvent.CreateInstance() };
GameMain.GameSession.EventManager.PinnedEvent = scriptedEvent.Last();
createEventButton = new GUIButton(new RectTransform(new Point(128, 64), GUI.Canvas, Anchor.TopCenter) { ScreenSpaceOffset = new Point(0, 32) }, TextManager.Get("create"))
{
OnClicked = delegate
{
scriptedEvent.Add(TriggeredEvent.CreateInstance());
GameMain.GameSession.EventManager.PinnedEvent = scriptedEvent.Last();
return true;
}
};
}
}
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
createEventButton?.AddToGUIUpdateList();
}
public override void End(CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
{
GameMain.GameSession.EventManager.PinnedEvent = null;
OnRoundEnd?.Invoke();
}
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (scriptedEvent != null)
{
foreach (Event sEvent in scriptedEvent.Where(sEvent => !sEvent.IsFinished))
{
sEvent.Update(deltaTime);
}
}
}
private void GenerateOutpost(Submarine submarine)
{
Submarine outpost = OutpostGenerator.Generate(OutpostParams ?? OutpostGenerationParams.Params.GetRandom(), OutpostType ?? LocationType.List.GetRandom());
outpost.SetPosition(Vector2.Zero);
float closestDistance = 0.0f;
DockingPort myPort = null, outPostPort = null;
foreach (DockingPort port in DockingPort.List)
{
if (port.IsHorizontal || port.Docked) { continue; }
if (port.Item.Submarine == outpost)
{
outPostPort = port;
continue;
}
if (port.Item.Submarine != submarine) { continue; }
//the submarine port has to be at the top of the sub
if (port.Item.WorldPosition.Y < submarine.WorldPosition.Y) { continue; }
float dist = Vector2.DistanceSquared(port.Item.WorldPosition, outpost.WorldPosition);
if ((myPort == null || dist < closestDistance || port.MainDockingPort) && !(myPort?.MainDockingPort ?? false))
{
myPort = port;
closestDistance = dist;
}
}
if (myPort != null && outPostPort != null)
{
Vector2 portDiff = myPort.Item.WorldPosition - submarine.WorldPosition;
Vector2 spawnPos = (outPostPort.Item.WorldPosition - portDiff) - Vector2.UnitY * outPostPort.DockedDistance;
submarine.SetPosition(spawnPos);
myPort.Dock(outPostPort);
myPort.Lock(true);
}
if (Character.Controlled != null)
{
Character.Controlled.TeleportTo(outpost.GetWaypoints(false).GetRandom(point => point.SpawnType == SpawnType.Human).WorldPosition);
}
}
}
}

View File

@@ -293,7 +293,9 @@ namespace Barotrauma.Tutorials
// Room 4
do { yield return null; } while (!officer_somethingBigSensor.MotionDetected);
TriggerTutorialSegment(3); // Arm railgun
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.SomethingBig"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(3); // Arm coilgun
do
{
SetHighlight(officer_coilgunLoader.Item, officer_coilgunLoader.Inventory.Items[0] == null || officer_coilgunLoader.Inventory.Items[0].Condition == 0);

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -373,15 +374,6 @@ namespace Barotrauma
}
}
if (!(gameSession.GameMode is CampaignMode))
{
var shadow = new GUIFrame(new RectTransform(new Point((int)(totalWidth * 1.2f), GameMain.GraphicsHeight * 2), background.RectTransform, Anchor.Center), style: "OuterGlow")
{
Color = Color.Black
};
shadow.RectTransform.SetAsFirstChild();
}
Frame = background;
return background;
}
@@ -511,7 +503,7 @@ namespace Barotrauma
Character character = characterInfo.Character;
if (character == null || character.IsDead)
{
if (characterInfo.IsNewHire)
if (character == null && characterInfo.IsNewHire)
{
statusText = TextManager.Get("CampaignCrew.NewHire");
statusColor = GUI.Style.Blue;
@@ -616,21 +608,8 @@ namespace Barotrauma
sliderHolder.RectTransform.MaxSize = new Point(int.MaxValue, GUI.IntScale(25.0f));
factionTextContent.Recalculate();
new GUICustomComponent(new RectTransform(new Vector2(0.8f, 1.0f), sliderHolder.RectTransform), onDraw: (sb, customComponent) =>
{
GUI.DrawRectangle(sb, customComponent.Rect, GUI.Style.ColorInventoryBackground, isFilled: true);
if (normalizedReputation < 0.5f)
{
int barWidth = (int)((0.5f - normalizedReputation) * customComponent.Rect.Width);
GUI.DrawRectangle(sb, new Rectangle(customComponent.Rect.Center.X - barWidth, customComponent.Rect.Y, barWidth, customComponent.Rect.Height), GUI.Style.Red, isFilled: true);
}
else if (normalizedReputation > 0.5f)
{
int barWidth = (int)((normalizedReputation - 0.5f) * customComponent.Rect.Width);
GUI.DrawRectangle(sb, new Rectangle(customComponent.Rect.Center.X, customComponent.Rect.Y, barWidth, customComponent.Rect.Height), GUI.Style.Green, isFilled: true);
}
GUI.DrawLine(sb, new Vector2(customComponent.Rect.Center.X, customComponent.Rect.Y - 2), new Vector2(customComponent.Rect.Center.X, customComponent.Rect.Bottom + 2), factionDescription.TextColor, width: 1);
});
new GUICustomComponent(new RectTransform(new Vector2(0.8f, 1.0f), sliderHolder.RectTransform),
onDraw: (sb, customComponent) => DrawReputationBar(sb, customComponent.Rect, normalizedReputation));
string reputationText = ((int)Math.Round(reputation)).ToString();
int reputationChange = (int)Math.Round( reputation - initialReputation);
@@ -650,5 +629,21 @@ namespace Barotrauma
textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont);
}
}
public static void DrawReputationBar(SpriteBatch sb, Rectangle rect, float normalizedReputation)
{
GUI.DrawRectangle(sb, rect, GUI.Style.ColorInventoryBackground, isFilled: true);
if (normalizedReputation < 0.5f)
{
int barWidth = (int)((0.5f - normalizedReputation) * rect.Width);
GUI.DrawRectangle(sb, new Rectangle(rect.Center.X - barWidth, rect.Y, barWidth, rect.Height), GUI.Style.Red, isFilled: true);
}
else if (normalizedReputation > 0.5f)
{
int barWidth = (int)((normalizedReputation - 0.5f) * rect.Width);
GUI.DrawRectangle(sb, new Rectangle(rect.Center.X, rect.Y, barWidth, rect.Height), GUI.Style.Green, isFilled: true);
}
GUI.DrawLine(sb, new Vector2(rect.Center.X, rect.Y - 2), new Vector2(rect.Center.X, rect.Bottom + 2), GUI.Style.TextColor);
}
}
}

View File

@@ -65,6 +65,10 @@ namespace Barotrauma
keyMapping[(int)InputType.Voice] = new KeyOrMouse(Keys.V);
keyMapping[(int)InputType.LocalVoice] = new KeyOrMouse(Keys.B);
keyMapping[(int)InputType.Command] = new KeyOrMouse(MouseButton.MiddleMouse);
#if DEBUG
keyMapping[(int)InputType.PreviousFireMode] = new KeyOrMouse(MouseButton.MouseWheelDown);
keyMapping[(int)InputType.NextFireMode] = new KeyOrMouse(MouseButton.MouseWheelUp);
#endif
if (Language == "French")
{
@@ -314,7 +318,7 @@ namespace Barotrauma
var corePackageDropdown = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform))
{
ButtonEnabled = ContentPackage.List.Count(cp => cp.CorePackage) > 1
ButtonEnabled = ContentPackage.CorePackages.Count > 1
};
var filterContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform), isHorizontal: true)
@@ -342,7 +346,7 @@ namespace Barotrauma
ScrollBarVisible = true
};
foreach (ContentPackage contentPackage in ContentPackage.List.Where(cp => cp.CorePackage))
foreach (ContentPackage contentPackage in ContentPackage.CorePackages)
{
var frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), corePackageDropdown.ListBox.Content.RectTransform), style: "ListBoxElement")
{
@@ -358,7 +362,7 @@ namespace Barotrauma
TextManager.GetWithVariables(contentPackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage",
new string[3] { "[packagename]", "[packageversion]", "[gameversion]" }, new string[3] { contentPackage.Name, contentPackage.GameVersion.ToString(), GameMain.Version.ToString() });
}
else if (contentPackage.CorePackage && !contentPackage.ContainsRequiredCorePackageFiles(out List<ContentType> missingContentTypes))
else if (!contentPackage.ContainsRequiredCorePackageFiles(out List<ContentType> missingContentTypes))
{
frame.UserData = null;
text.TextColor = GUI.Style.Red * 0.6f;
@@ -374,7 +378,7 @@ namespace Barotrauma
"\n" + string.Join("\n", contentPackage.ErrorMessages);
}
if (SelectedContentPackages.Contains(contentPackage))
if (contentPackage == CurrentCorePackage)
{
corePackageDropdown.Select(corePackageDropdown.ListBox.Content.GetChildIndex(frame));
}
@@ -382,10 +386,7 @@ namespace Barotrauma
corePackageDropdown.OnSelected = SelectCorePackage;
corePackageDropdown.ListBox.CanBeFocused = CanHotswapPackages(true);
foreach (ContentPackage contentPackage in ContentPackage.List
.Where(cp => !cp.CorePackage)
.OrderBy(cp => !SelectedContentPackages.Contains(cp))
.ThenBy(cp => -SelectedContentPackages.IndexOf(cp)))
foreach (ContentPackage contentPackage in ContentPackage.RegularPackages)
{
var frame = new GUIFrame(new RectTransform(new Vector2(1.0f, tickBoxScale.Y), contentPackageList.Content.RectTransform), style: "ListBoxElement")
{
@@ -407,7 +408,7 @@ namespace Barotrauma
style: "GUITickBox")
{
UserData = contentPackage,
Selected = SelectedContentPackages.Contains(contentPackage),
Selected = EnabledRegularPackages.Contains(contentPackage),
OnSelected = SelectContentPackage,
Enabled = CanHotswapPackages(false)
};
@@ -1595,10 +1596,10 @@ namespace Barotrauma
if (userData is ContentPackage contentPackage)
{
if (!SelectedContentPackages.Contains(contentPackage)) { return; }
if (!EnabledRegularPackages.Contains(contentPackage)) { return; }
}
ReorderSelectedContentPackages(cp => -listBox.Content.GetChildIndex(listBox.Content.GetChildByUserData(cp)));
ContentPackage.SortContentPackages(cp => listBox.Content.GetChildIndex(listBox.Content.GetChildByUserData(cp)), true);
UnsavedSettings = true;
}
@@ -1609,18 +1610,13 @@ namespace Barotrauma
var contentPackage = tickBox.UserData as ContentPackage;
ContentPackage.List = ContentPackage.List
.OrderByDescending(p => p.CorePackage)
.ThenBy(cp => -contentPackageList.Content.GetChildIndex(contentPackageList.Content.GetChildByUserData(cp)))
.ToList();
if (tickBox.Selected)
{
SelectContentPackage(contentPackage);
EnableRegularPackage(contentPackage);
}
else
{
DeselectContentPackage(contentPackage);
DisableRegularPackage(contentPackage);
}
UnsavedSettings = true;

View File

@@ -717,7 +717,7 @@ namespace Barotrauma
{
slot.QuickUseButtonToolTip = quickUseAction == QuickUseAction.None ?
"" : TextManager.GetWithVariable("QuickUseAction." + quickUseAction.ToString(), "[equippeditem]", character.SelectedItems.FirstOrDefault(i => i != null)?.Name);
if (PlayerInput.PrimaryMouseButtonDown()) slot.EquipButtonState = GUIComponent.ComponentState.Pressed;
if (PlayerInput.PrimaryMouseButtonDown()) { slot.EquipButtonState = GUIComponent.ComponentState.Pressed; }
if (PlayerInput.PrimaryMouseButtonClicked())
{
QuickUseItem(item, allowEquip: true, allowInventorySwap: false, allowApplyTreatment: false);
@@ -937,28 +937,52 @@ namespace Barotrauma
switch (quickUseAction)
{
case QuickUseAction.Equip:
//attempt to put in a free slot first
for (int i = capacity - 1; i >= 0; i--)
if (string.IsNullOrEmpty(item.Prefab.EquipConfirmationText) || character != Character.Controlled)
{
if (Items[i] != null) { continue; }
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; }
success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) { break; }
Equip();
}
else
{
if (GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "equipconfirmation")) { return; }
var equipConfirmation = new GUIMessageBox(string.Empty, TextManager.Get(item.Prefab.EquipConfirmationText),
new string[] { TextManager.Get("yes"), TextManager.Get("no") })
{
UserData = "equipconfirmation"
};
equipConfirmation.Buttons[0].OnClicked = (btn, userdata) =>
{
Equip();
equipConfirmation.Close();
return true;
};
equipConfirmation.Buttons[1].OnClicked = equipConfirmation.Close;
}
if (!success)
void Equip()
{
//attempt to put in a free slot first
for (int i = capacity - 1; i >= 0; i--)
{
if (Items[i] != null) { continue; }
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; }
// something else already equipped in a hand slot, attempt to unequip it so items aren't unnecessarily swapped to it
if (Items[i] != null && Items[i].AllowedSlots.Contains(InvSlotType.Any) && (SlotTypes[i] == InvSlotType.LeftHand || SlotTypes[i] == InvSlotType.RightHand))
{
TryPutItem(Items[i], Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true);
}
success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) { break; }
}
if (!success)
{
for (int i = capacity - 1; i >= 0; i--)
{
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; }
// something else already equipped in a hand slot, attempt to unequip it so items aren't unnecessarily swapped to it
if (Items[i] != null && Items[i].AllowedSlots.Contains(InvSlotType.Any) && (SlotTypes[i] == InvSlotType.LeftHand || SlotTypes[i] == InvSlotType.RightHand))
{
TryPutItem(Items[i], Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true);
}
success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) { break; }
}
}
}
break;
case QuickUseAction.Unequip:
@@ -1046,8 +1070,8 @@ namespace Barotrauma
public void DrawOwn(SpriteBatch spriteBatch)
{
if (!AccessibleWhenAlive && !character.IsDead) return;
if (slots == null) CreateSlots();
if (!AccessibleWhenAlive && !character.IsDead) { return; }
if (slots == null) { CreateSlots(); }
if (GameMain.GraphicsWidth != screenResolution.X ||
GameMain.GraphicsHeight != screenResolution.Y ||
prevUIScale != UIScale ||
@@ -1070,7 +1094,7 @@ namespace Barotrauma
for (int i = 0; i < capacity; i++)
{
if (HideSlot(i)) continue;
if (HideSlot(i)) { continue; }
Rectangle interactRect = slots[i].InteractRect;
interactRect.Location += slots[i].DrawOffset.ToPoint();
@@ -1086,9 +1110,13 @@ namespace Barotrauma
}
InventorySlot highlightedQuickUseSlot = null;
Rectangle inventoryArea = Rectangle.Empty;
for (int i = 0; i < capacity; i++)
{
if (HideSlot(i)) continue;
if (HideSlot(i)) { continue; }
inventoryArea = inventoryArea == Rectangle.Empty ? slots[i].InteractRect : Rectangle.Union(inventoryArea, slots[i].InteractRect);
if (Items[i] == null ||
(draggingItem == Items[i] && !slots[i].InteractRect.Contains(PlayerInput.MousePosition)) ||
@@ -1102,7 +1130,7 @@ namespace Barotrauma
}
continue;
}
if (draggingItem == Items[i] && !slots[i].IsHighlighted) continue;
if (draggingItem == Items[i] && !slots[i].IsHighlighted) { continue; }
//draw hand icons if the item is equipped in a hand slot
if (IsInLimbSlot(Items[i], InvSlotType.LeftHand))
@@ -1169,7 +1197,17 @@ namespace Barotrauma
}
}
if (highlightedQuickUseSlot != null && !string.IsNullOrEmpty(highlightedQuickUseSlot.QuickUseButtonToolTip))
if (Locked)
{
GUI.DrawRectangle(spriteBatch, inventoryArea, new Color(30,30,30,100), isFilled: true);
var lockIcon = GUI.Style.GetComponentStyle("LockIcon")?.GetDefaultSprite();
lockIcon?.Draw(spriteBatch, inventoryArea.Center.ToVector2(), scale: Math.Min(inventoryArea.Height / lockIcon.size.Y * 0.7f, 1.0f));
if (inventoryArea.Contains(PlayerInput.MousePosition))
{
GUIComponent.DrawToolTip(spriteBatch, TextManager.Get("handcuffed"), new Rectangle(inventoryArea.Center - new Point(inventoryArea.Height / 2), new Point(inventoryArea.Height)));
}
}
else if (highlightedQuickUseSlot != null && !string.IsNullOrEmpty(highlightedQuickUseSlot.QuickUseButtonToolTip))
{
GUIComponent.DrawToolTip(spriteBatch, highlightedQuickUseSlot.QuickUseButtonToolTip, highlightedQuickUseSlot.EquipButtonRect);
}

View File

@@ -68,29 +68,52 @@ namespace Barotrauma.Items.Components
if (Window.Height > 0 && Window.Width > 0)
{
rect.Height = -(int)(Window.Y * item.Scale);
rect.Y += (int)(doorRect.Height * openState);
rect.Height = Math.Max(rect.Height - (rect.Y - doorRect.Y), 0);
rect.Y = Math.Min(doorRect.Y, rect.Y);
if (convexHull2 != null)
if (IsHorizontal)
{
Rectangle rect2 = doorRect;
rect2.Y += (int)(Window.Y * item.Scale - Window.Height * item.Scale);
rect2.Y += (int)(doorRect.Height * openState);
rect2.Y = Math.Min(doorRect.Y, rect2.Y);
rect2.Height = rect2.Y - (doorRect.Y - (int)(doorRect.Height * (1.0f - openState)));
if (rect2.Height == 0)
rect.Width = (int)(Window.X * item.Scale);
rect.X -= (int)(doorRect.Width * openState);
rect.Width = Math.Max(rect.Width - (doorRect.X - rect.X), 0);
rect.X = Math.Max(doorRect.X, rect.X);
if (convexHull2 != null)
{
convexHull2.Enabled = false;
Rectangle rect2 = doorRect;
rect2.X += (int)(Window.Right * item.Scale);
rect2.X -= (int)(doorRect.Width * openState);
rect2.X = Math.Max(doorRect.X, rect2.X);
rect2.Width = doorRect.Right - (int)(doorRect.Width * openState) - rect2.X;
if (rect2.Width == 0)
{
convexHull2.Enabled = false;
}
else
{
convexHull2.Enabled = true;
convexHull2.SetVertices(GetConvexHullCorners(rect2));
}
}
else
}
else
{
rect.Height = -(int)(Window.Y * item.Scale);
rect.Y += (int)(doorRect.Height * openState);
rect.Height = Math.Max(rect.Height - (rect.Y - doorRect.Y), 0);
rect.Y = Math.Min(doorRect.Y, rect.Y);
if (convexHull2 != null)
{
convexHull2.Enabled = true;
convexHull2.SetVertices(GetConvexHullCorners(rect2));
Rectangle rect2 = doorRect;
rect2.Y += (int)(Window.Y * item.Scale - Window.Height * item.Scale);
rect2.Y += (int)(doorRect.Height * openState);
rect2.Y = Math.Min(doorRect.Y, rect2.Y);
rect2.Height = rect2.Y - (doorRect.Y - (int)(doorRect.Height * (1.0f - openState)));
if (rect2.Height == 0)
{
convexHull2.Enabled = false;
}
else
{
convexHull2.Enabled = true;
convexHull2.SetVertices(GetConvexHullCorners(rect2));
}
}
}
}
@@ -251,8 +274,9 @@ namespace Barotrauma.Items.Components
bool open = msg.ReadBoolean();
bool broken = msg.ReadBoolean();
bool forcedOpen = msg.ReadBoolean();
bool isStuck = msg.ReadBoolean();
SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen);
Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
UInt16 lastUserID = msg.ReadUInt16();
Character user = lastUserID == 0 ? null : Entity.FindEntityByID(lastUserID) as Character;
if (user != lastUser)
@@ -260,7 +284,7 @@ namespace Barotrauma.Items.Components
lastUser = user;
toggleCooldownTimer = ToggleCoolDown;
}
this.isStuck = isStuck;
if (isStuck) { OpenState = 0.0f; }
IsBroken = broken;
PredictedState = null;

View File

@@ -0,0 +1,360 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
{
internal partial class VineTile
{
public void Draw(SpriteBatch spriteBatch, Vector2 position, float depth, float leafDepth)
{
Vector2 pos = position + Position;
pos.Y = -pos.Y;
VineSprite vineSprite = Parent.VineSprites[Type];
Color color = Parent.Decayed ? Parent.DeadTint : Parent.VineTint;
float layer1 = depth + 0.01f, // flowers
layer2 = depth + 0.02f, // decay atlas
layer3 = depth + 0.03f; // branches and leaves
float scale = Parent.VineScale * VineStep;
if (Parent.VineAtlas != null)
{
spriteBatch.Draw(Parent.VineAtlas.Texture, pos + offset, vineSprite.SourceRect, color, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer3);
}
if (Parent.DecayAtlas != null)
{
spriteBatch.Draw(Parent.DecayAtlas.Texture, pos, vineSprite.SourceRect, HealthColor, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer2);
}
if (FlowerConfig.Variant >= 0 && !Parent.Decayed)
{
Sprite flowerSprite = Parent.FlowerSprites[FlowerConfig.Variant];
flowerSprite.Draw(spriteBatch, pos, Parent.FlowerTint, flowerSprite.Origin, scale: Parent.BaseFlowerScale * FlowerConfig.Scale * FlowerStep, rotate: FlowerConfig.Rotation, depth: layer1);
}
if (LeafConfig.Variant >= 0)
{
Sprite leafSprite = Parent.LeafSprites[LeafConfig.Variant];
leafSprite.Draw(spriteBatch, pos, Parent.Decayed ? Parent.DeadTint : Parent.LeafTint, leafSprite.Origin, scale: Parent.BaseLeafScale * LeafConfig.Scale * FlowerStep, rotate: LeafConfig.Rotation, depth: layer3 + leafDepth);
}
}
}
internal class VineSprite
{
[Serialize("0,0,0,0", false)]
public Rectangle SourceRect { get; private set; }
[Serialize("0.5,0.5", false)]
public Vector2 Origin { get; private set; }
public Vector2 AbsoluteOrigin;
public VineSprite(XElement element)
{
SerializableProperty.DeserializeProperties(this, element);
AbsoluteOrigin = new Vector2(SourceRect.Width * Origin.X, SourceRect.Height * Origin.Y);
}
}
internal partial class Growable
{
public readonly Dictionary<VineTileType, VineSprite> VineSprites = new Dictionary<VineTileType, VineSprite>();
public readonly List<Sprite> FlowerSprites = new List<Sprite>();
public readonly List<Sprite> LeafSprites = new List<Sprite>();
public Sprite? VineAtlas, DecayAtlas;
protected override void RemoveComponentSpecific()
{
VineAtlas?.Remove();
DecayAtlas?.Remove();
foreach (Sprite sprite in FlowerSprites)
{
sprite.Remove();
}
foreach (Sprite sprite in LeafSprites)
{
sprite.Remove();
}
}
public void Draw(SpriteBatch spriteBatch, Planter planter, Vector2 offset, float depth)
{
const float zStep = 0.0001f;
float leafDepth = 0f;
foreach (VineTile vine in Vines)
{
leafDepth += zStep;
vine.Draw(spriteBatch, planter.Item.DrawPosition + offset, depth, leafDepth);
}
if (GameMain.DebugDraw)
{
foreach (Rectangle rect in FailedRectangles)
{
Rectangle wRect = rect;
wRect.Y = -wRect.Y;
wRect.Y -= wRect.Height;
GUI.DrawRectangle(spriteBatch, wRect, Color.Red);
}
}
}
partial void LoadVines(XElement element)
{
string? vineAtlasPath = element.GetAttributeString("vineatlas", null);
string? decayAtlasPath = element.GetAttributeString("decayatlas", null);
if (vineAtlasPath != null)
{
VineAtlas = new Sprite(vineAtlasPath, Rectangle.Empty);
}
if (decayAtlasPath != null)
{
DecayAtlas = new Sprite(decayAtlasPath, Rectangle.Empty);
}
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "vinesprite":
var tileType = subElement.GetAttributeString("type", null);
VineTileType type = Enum.Parse<VineTileType>(tileType);
VineSprites.Add(type, new VineSprite(subElement));
break;
case "flowersprite":
FlowerSprites.Add(new Sprite(subElement));
break;
case "leafsprite":
LeafSprites.Add(new Sprite(subElement));
break;
}
flowerVariants = FlowerSprites.Count;
leafVariants = LeafSprites.Count;
}
foreach (VineTileType type in Enum.GetValues(typeof(VineTileType)))
{
if (!VineSprites.ContainsKey(type))
{
DebugConsole.ThrowError($"Vine sprite missing from {item.prefab.Identifier}: {type}");
}
}
}
private readonly object mutex = new object();
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
Health = msg.ReadRangedSingle(0, MaxHealth, 8);
int startOffset = msg.ReadRangedInteger(-1, MaximumVines);
if (startOffset > -1)
{
int vineCount = msg.ReadRangedInteger(0, VineChunkSize);
List<VineTile> tiles = new List<VineTile>();
for (int i = 0; i < vineCount; i++)
{
VineTileType vineType = (VineTileType) msg.ReadRangedInteger(0b0000, 0b1111);
int flowerConfig = msg.ReadRangedInteger(0, 0xFFF);
int leafConfig = msg.ReadRangedInteger(0, 0xFFF);
sbyte posX = (sbyte) msg.ReadByte(), posY = (sbyte) msg.ReadByte();
Vector2 pos = new Vector2(posX * VineTile.Size, posY * VineTile.Size);
tiles.Add(new VineTile(this, pos, vineType, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafConfig)));
}
// is this even needed??
lock (mutex)
{
for (var i = 0; i < vineCount; i++)
{
int index = i + startOffset;
if (index >= Vines.Count)
{
Vines.Add(tiles[i]);
continue;
}
VineTile oldVine = Vines[index];
VineTile newVine = tiles[i];
newVine.GrowthStep = oldVine.GrowthStep;
Vines[index] = newVine;
}
}
}
UpdateBranchHealth();
ResetPlanterSize();
}
private void ResetPlanterSize()
{
if (item.ParentInventory is ItemInventory itemInventory && itemInventory.Owner is Item parentItem)
{
if (parentItem.GetComponent<Planter>() is { } planter)
{
planter.Item.ResetCachedVisibleSize();
}
}
}
#if DEBUG
private int seed;
// Huge bowl of spaghetti
public void CreateDebugHUD(Planter planter, PlantSlot slot)
{
Vector2 relativeSize = new Vector2(0.3f, 0.6f);
GUIMessageBox msgBox = new GUIMessageBox(item.Name, "", new[] { TextManager.Get("applysettingsbutton") }, relativeSize);
GUILayoutGroup content = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.85f), msgBox.Content.RectTransform)) { Stretch = true };
GUINumberInput seedInput = CreateIntEntry("Random Seed", seed, content.RectTransform);
GUINumberInput vineTileSizeInput = CreateIntEntry("Vine Tile Size (Global)", VineTile.Size, content.RectTransform);
GUINumberInput[] leafScaleRangeInput = CreateMinMaxEntry("Leaf Scale Range (Global)", new []{ MinLeafScale, MaxLeafScale }, 1.5f, content.RectTransform);
GUINumberInput[] flowerScaleRangeInput = CreateMinMaxEntry("Flower Scale Range (Global)", new []{ MinFlowerScale, MaxFlowerScale }, 1.5f, content.RectTransform);
GUINumberInput vineCountInput = CreateIntEntry("Vine Count", MaximumVines, content.RectTransform);
GUINumberInput vineScaleInput = CreateFloatEntry("Vine Scale", VineScale, content.RectTransform);
GUINumberInput flowerInput = CreateIntEntry("Flower Quantity", FlowerQuantity, content.RectTransform);
GUINumberInput flowerScaleInput = CreateFloatEntry("Flower Scale", BaseFlowerScale, content.RectTransform);
GUINumberInput leafScaleInput = CreateFloatEntry("Leaf Scale", BaseLeafScale, content.RectTransform);
GUINumberInput leafProbabilityInput = CreateFloatEntry("Leaf Probability", LeafProbability, content.RectTransform);
GUINumberInput[] leafTintInputs = CreateMinMaxEntry("Leaf Tint", new []{ LeafTint.R / 255f, LeafTint.G / 255f, LeafTint.B / 255f }, 1.0f, content.RectTransform);
GUINumberInput[] flowerTintInputs = CreateMinMaxEntry("Flower Tint", new []{ FlowerTint.R / 255f, FlowerTint.G / 255f, FlowerTint.B / 255f }, 1.0f, content.RectTransform);
GUINumberInput[] vineTintInputs = CreateMinMaxEntry("Branch Tint", new []{ VineTint.R / 255f, VineTint.G / 255f, VineTint.B / 255f }, 1.0f, content.RectTransform);
// Apply
msgBox.Buttons[0].OnClicked = (button, o) =>
{
seed = seedInput.IntValue;
MaximumVines = vineCountInput.IntValue;
FlowerQuantity = flowerInput.IntValue;
BaseFlowerScale = flowerScaleInput.FloatValue;
VineScale = vineScaleInput.FloatValue;
BaseLeafScale = leafScaleInput.FloatValue;
LeafProbability = leafProbabilityInput.FloatValue;
VineTile.Size = vineTileSizeInput.IntValue;
MinFlowerScale = flowerScaleRangeInput[0].FloatValue;
MaxFlowerScale = flowerScaleRangeInput[1].FloatValue;
MinLeafScale = leafScaleRangeInput[0].FloatValue;
MaxLeafScale = leafScaleRangeInput[1].FloatValue;
LeafTint = new Color(leafTintInputs[0].FloatValue, leafTintInputs[1].FloatValue, leafTintInputs[2].FloatValue);
FlowerTint = new Color(flowerTintInputs[0].FloatValue, flowerTintInputs[1].FloatValue, flowerTintInputs[2].FloatValue);
VineTint = new Color(vineTintInputs[0].FloatValue, vineTintInputs[1].FloatValue, vineTintInputs[2].FloatValue);
if (FlowerQuantity >= MaximumVines - 1)
{
vineCountInput.Flash(Color.Red);
flowerInput.Flash(Color.Red);
return false;
}
if (MinFlowerScale > MaxFlowerScale)
{
foreach (GUINumberInput input in flowerScaleRangeInput)
{
input.Flash(Color.Red);
}
return false;
}
if (MinLeafScale > MaxLeafScale)
{
foreach (GUINumberInput input in leafScaleRangeInput)
{
input.Flash(Color.Red);
}
return false;
}
msgBox.Close();
Random random = new Random(seed);
Random flowerRandom = new Random(seed);
Vines.Clear();
GenerateFlowerTiles(flowerRandom);
GenerateStem();
Decayed = false;
FullyGrown = false;
while (MaximumVines > Vines.Count)
{
if (!CanGrowMore())
{
Decayed = true;
break;
}
TryGenerateBranches(planter, slot, random, flowerRandom);
}
if (!Decayed)
{
FullyGrown = true;
}
foreach (VineTile vineTile in Vines)
{
vineTile.GrowthStep = 2.0f;
}
return true;
};
}
private static GUINumberInput CreateIntEntry(string label, int defaultValue, RectTransform parent)
{
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f), parent), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), label);
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), GUINumberInput.NumberType.Int) { IntValue = defaultValue };
return input;
}
private static GUINumberInput CreateFloatEntry(string label, float defaultValue, RectTransform parent)
{
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f), parent), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), label);
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), GUINumberInput.NumberType.Float) { FloatValue = defaultValue, DecimalsToDisplay = 2 };
return input;
}
private static GUINumberInput[] CreateMinMaxEntry(string label, float[] values, float max, RectTransform parent, float min = 0f)
{
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f), parent), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), label);
GUINumberInput[] inputs = new GUINumberInput[values.Length];
for (var i = 0; i < values.Length; i++)
{
float value = values[i];
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f / values.Length, 1f), layout.RectTransform), GUINumberInput.NumberType.Float)
{
FloatValue = value, DecimalsToDisplay = 2,
MinValueFloat = min,
MaxValueFloat = max
};
inputs[i] = input;
}
return inputs;
}
#endif
}
}

View File

@@ -12,11 +12,11 @@ namespace Barotrauma.Items.Components
{
partial class RangedWeapon : ItemComponent
{
private Sprite crosshairSprite, crosshairPointerSprite;
protected Sprite crosshairSprite, crosshairPointerSprite;
private Vector2 crosshairPos, crosshairPointerPos;
protected Vector2 crosshairPos, crosshairPointerPos;
private float currentCrossHairScale, currentCrossHairPointerScale;
protected float currentCrossHairScale, currentCrossHairPointerScale;
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
@@ -86,7 +86,6 @@ namespace Barotrauma.Items.Components
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
if (crosshairSprite == null) { return; }
if (character == null || !character.IsKeyDown(InputType.Aim)) { return; }
//camera focused on some other item/device, don't draw the crosshair

View File

@@ -0,0 +1,338 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Sprayer : RangedWeapon, IDrawableComponent
{
#if DEBUG
private Vector2 debugRayStartPos, debugRayEndPos;
#endif
public Vector2 DrawSize
{
get { return Vector2.Zero; }
}
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
private Hull targetHull;
private Vector2 rayStartWorldPosition;
private Color color;
partial void InitProjSpecific(XElement element)
{
currentCrossHairPointerScale = element.GetAttributeFloat("crosshairscale", 0.1f);
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "particleemitter":
particleEmitters.Add(new ParticleEmitter(subElement));
break;
}
}
}
private readonly List<BackgroundSection> targetSections = new List<BackgroundSection>();
// 0 = 1x1, 1 = 2x2, 2 = 3x3
private int spraySetting = 0;
private readonly Point[] sprayArray = new Point[8];
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
if (character == null || !character.IsKeyDown(InputType.Aim)) return;
#if DEBUG
if (PlayerInput.KeyHit(InputType.PreviousFireMode))
#else
if (PlayerInput.MouseWheelDownClicked())
#endif
{
if (spraySetting > 0)
{
spraySetting--;
}
else
{
spraySetting = 2;
}
targetSections.Clear();
}
#if DEBUG
if (PlayerInput.KeyHit(InputType.NextFireMode))
#else
if (PlayerInput.MouseWheelUpClicked())
#endif
{
if (spraySetting < 2)
{
spraySetting++;
}
else
{
spraySetting = 0;
}
targetSections.Clear();
}
crosshairPointerPos = PlayerInput.MousePosition;
Vector2 rayStart;
Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos;
Vector2 barrelPos = item.SimPosition + TransformedBarrelPos;
//make sure there's no obstacles between the base of the item (or the shoulder of the character) and the end of the barrel
if (Submarine.PickBody(sourcePos, barrelPos, collisionCategory: Physics.CollisionItem | Physics.CollisionItemBlocking | Physics.CollisionWall) == null)
{
//no obstacles -> we start the raycast at the end of the barrel
rayStart = ConvertUnits.ToSimUnits(item.WorldPosition) + TransformedBarrelPos;
}
else
{
targetHull = null;
targetSections.Clear();
return;
}
Vector2 pos = character.CursorWorldPosition;
Vector2 rayEnd = ConvertUnits.ToSimUnits(pos);
rayStartWorldPosition = ConvertUnits.ToDisplayUnits(rayStart);
if (Vector2.Distance(rayStartWorldPosition, pos) > Range)
{
targetHull = null;
targetSections.Clear();
return;
}
#if DEBUG
debugRayStartPos = ConvertUnits.ToDisplayUnits(rayStart);
debugRayEndPos = ConvertUnits.ToDisplayUnits(rayEnd);
#endif
Submarine parentSub = character.Submarine ?? item.Submarine;
if (parentSub != null)
{
rayStart -= parentSub.SimPosition;
rayEnd -= parentSub.SimPosition;
}
var obstacles = Submarine.PickBodies(rayStart, rayEnd, collisionCategory: Physics.CollisionItem | Physics.CollisionItemBlocking | Physics.CollisionWall);
foreach (var body in obstacles)
{
if (body.UserData is Item item)
{
var door = item.GetComponent<Door>();
if (door != null && door.IsOpen || door.IsBroken) continue;
}
targetHull = null;
targetSections.Clear();
return;
}
targetHull = Hull.GetCleanTarget(pos);
if (targetHull == null)
{
targetSections.Clear();
return;
}
BackgroundSection mousedOverSection = targetHull.GetBackgroundSection(pos);
if (mousedOverSection == null)
{
targetSections.Clear();
return;
}
// No need to refresh
if (targetSections.Count > 0 && mousedOverSection == targetSections[0])
{
return;
}
targetSections.Clear();
targetSections.Add(mousedOverSection);
int mousedOverIndex = mousedOverSection.Index;
// Start with 2x2
if (spraySetting > 0)
{
sprayArray[0].X = mousedOverIndex + 1;
sprayArray[0].Y = mousedOverSection.RowIndex;
sprayArray[1].X = mousedOverIndex + targetHull.xBackgroundMax;
sprayArray[1].Y = mousedOverSection.RowIndex + 1;
sprayArray[2].X = sprayArray[1].X + 1;
sprayArray[2].Y = sprayArray[1].Y;
for (int i = 0; i < 3; i++)
{
if (targetHull.DoesSectionMatch(sprayArray[i].X, sprayArray[i].Y))
{
targetSections.Add(targetHull.BackgroundSections[sprayArray[i].X]);
}
}
// Add more if it's 3x3
if (spraySetting == 2)
{
sprayArray[3].X = mousedOverIndex - 1;
sprayArray[3].Y = mousedOverSection.RowIndex;
sprayArray[4].X = sprayArray[1].X - 1;
sprayArray[4].Y = sprayArray[1].Y;
sprayArray[5].X = sprayArray[3].X - targetHull.xBackgroundMax;
sprayArray[5].Y = sprayArray[3].Y - 1;
sprayArray[6].X = sprayArray[5].X + 1;
sprayArray[6].Y = sprayArray[5].Y;
sprayArray[7].X = sprayArray[6].X + 1;
sprayArray[7].Y = sprayArray[6].Y;
for (int i = 3; i < sprayArray.Length; i++)
{
if (targetHull.DoesSectionMatch(sprayArray[i].X, sprayArray[i].Y))
{
targetSections.Add(targetHull.BackgroundSections[sprayArray[i].X]);
}
}
}
}
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
if (character == null || !character.IsKeyDown(InputType.Aim)) { return; }
GUI.HideCursor = targetSections.Count > 0;
}
public override bool Use(float deltaTime, Character character = null)
{
if (character == null) { return false; }
if (character == Character.Controlled)
{
if (targetSections.Count == 0) { return false; }
Spray(deltaTime);
return true;
}
else
{
//allow remote players to use the sprayer, but don't actually color the walls (we'll receive the data from the server)
return character.IsRemotePlayer;
}
}
public void Spray(float deltaTime)
{
if (targetSections.Count == 0) { return; }
Item liquidItem = liquidContainer?.Inventory.Items[0];
if (liquidItem == null) { return; }
bool isCleaning = false;
liquidColors.TryGetValue(liquidItem.prefab.Identifier, out color);
// Ethanol or other cleaning solvent
if (color.A == 0) { isCleaning = true; }
float sizeAdjustedSprayStrength = SprayStrength / targetSections.Count;
if (!isCleaning)
{
for (int i = 0; i < targetSections.Count; i++)
{
targetHull.SetSectionColorOrStrength(targetSections[i], color, sizeAdjustedSprayStrength * deltaTime, true, false);
}
}
else
{
for (int i = 0; i < targetSections.Count; i++)
{
targetHull.CleanSection(targetSections[i], -sizeAdjustedSprayStrength * deltaTime, true);
}
}
Vector2 particleStartPos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);
Vector2 particleEndPos = Vector2.Zero;
for (int i = 0; i < targetSections.Count; i++)
{
particleEndPos += new Vector2(targetSections[i].Rect.Center.X, targetSections[i].Rect.Y - targetSections[i].Rect.Height / 2) + targetHull.Rect.Location.ToVector2();
}
particleEndPos /= targetSections.Count;
if (targetHull?.Submarine != null)
{
particleEndPos += targetHull.Submarine.Position;
}
float dist = Vector2.Distance(particleStartPos, particleEndPos);
foreach (ParticleEmitter particleEmitter in particleEmitters)
{
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
float particleRange = particleEmitter.Prefab.VelocityMax * particleEmitter.Prefab.ParticlePrefab.LifeTime;
particleEmitter.Emit(
deltaTime, particleStartPos,
item.CurrentHull, particleAngle, particleEmitter.Prefab.CopyEntityAngle ? -particleAngle : 0, velocityMultiplier: dist / particleRange * 1.5f,
colorMultiplier: new Color(color.R, color.G, color.B, (byte)255));
}
}
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
#if DEBUG
if (GameMain.DebugDraw && Character.Controlled != null && Character.Controlled.IsKeyDown(InputType.Aim))
{
GUI.DrawLine(spriteBatch,
new Vector2(debugRayStartPos.X, -debugRayStartPos.Y),
new Vector2(debugRayEndPos.X, -debugRayEndPos.Y),
Color.Yellow);
}
#endif
if (Character.Controlled == null || !Character.Controlled.HasEquippedItem(item) || !Character.Controlled.IsKeyDown(InputType.Aim) || targetHull == null || targetSections.Count == 0) return;
Vector2 drawOffset = targetHull.Submarine == null ? Vector2.Zero : targetHull.Submarine.DrawPosition;
Point sectionSize = targetSections[0].Rect.Size;
Rectangle drawPositionRect = new Rectangle((int)(drawOffset.X + targetHull.Rect.X), (int)(drawOffset.Y + targetHull.Rect.Y), sectionSize.X, sectionSize.Y);
if (crosshairSprite == null && crosshairPointerSprite == null)
{
for (int i = 0; i < targetSections.Count; i++)
{
GUI.DrawRectangle(spriteBatch, new Vector2(drawPositionRect.X + targetSections[i].Rect.X, -(drawPositionRect.Y + targetSections[i].Rect.Y)), new Vector2(sectionSize.X, sectionSize.Y), Color.White, false, 0.0f, 1);
}
}
else if (targetSections.Count > 0)
{
Vector2 drawPos = Vector2.Zero;
for (int i = 0; i < targetSections.Count; i++)
{
drawPos += new Vector2(drawPositionRect.X + targetSections[i].Rect.X + sectionSize.X / 2, -(drawPositionRect.Y + targetSections[i].Rect.Y - sectionSize.Y / 2));
}
drawPos /= targetSections.Count;
crosshairSprite?.Draw(spriteBatch, drawPos, scale: sectionSize.X * 3 / crosshairSprite.size.X);
crosshairPointerSprite?.Draw(spriteBatch, drawPos, scale: sectionSize.X * (spraySetting + 1) / crosshairPointerSprite.size.X);
}
}
}
}

View File

@@ -256,6 +256,7 @@ namespace Barotrauma.Items.Components
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
new Vector3(item.WorldPosition, 0.0f),
0.01f,
loopingSound.RoundSound.GetRandomFrequencyMultiplier(),
SoundPlayer.ShouldMuffleSound(Character.Controlled, item.WorldPosition, loopingSound.Range, Character.Controlled?.CurrentHull));
loopingSoundChannel.Looping = true;
//TODO: tweak
@@ -326,7 +327,7 @@ namespace Barotrauma.Items.Components
{
float volume = GetSoundVolume(itemSound);
if (volume <= 0.0001f) { return; }
var channel = SoundPlayer.PlaySound(itemSound.RoundSound.Sound, position, volume, itemSound.Range, item.CurrentHull);
var channel = SoundPlayer.PlaySound(itemSound.RoundSound.Sound, position, volume, itemSound.Range, itemSound.RoundSound.GetRandomFrequencyMultiplier(), item.CurrentHull);
if (channel != null) { playingOneshotSoundChannels.Add(channel); }
}
}

View File

@@ -172,6 +172,8 @@ namespace Barotrauma.Items.Components
{
Vector2 transformedItemPos = ItemPos * item.Scale;
Vector2 transformedItemInterval = ItemInterval * item.Scale;
Vector2 transformedItemIntervalHorizontal = new Vector2(transformedItemInterval.X, 0.0f);
Vector2 transformedItemIntervalVertical = new Vector2(0.0f, transformedItemInterval.Y);
if (item.body == null)
{
@@ -180,15 +182,26 @@ namespace Barotrauma.Items.Components
transformedItemPos.X = -transformedItemPos.X;
transformedItemPos.X += item.Rect.Width;
transformedItemInterval.X = -transformedItemInterval.X;
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
}
if (item.FlippedY)
{
transformedItemPos.Y = -transformedItemPos.Y;
transformedItemPos.Y -= item.Rect.Height;
transformedItemInterval.Y = -transformedItemInterval.Y;
transformedItemIntervalVertical.Y = -transformedItemIntervalVertical.Y;
}
transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y);
if (item.Submarine != null) { transformedItemPos += item.Submarine.DrawPosition; }
if (Math.Abs(item.Rotation) > 0.01f)
{
Matrix transform = Matrix.CreateRotationZ(MathHelper.ToRadians(-item.Rotation));
transformedItemPos = Vector2.Transform(transformedItemPos - item.DrawPosition, transform) + item.DrawPosition;
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemIntervalVertical = Vector2.Transform(transformedItemIntervalVertical, transform);
}
}
else
{
@@ -197,9 +210,12 @@ namespace Barotrauma.Items.Components
{
transformedItemPos.X = -transformedItemPos.X;
transformedItemInterval.X = -transformedItemInterval.X;
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
}
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemPos += item.DrawPosition;
}
@@ -207,8 +223,14 @@ namespace Barotrauma.Items.Components
Vector2 currentItemPos = transformedItemPos;
SpriteEffects spriteEffects = SpriteEffects.None;
if ((item.body != null && item.body.Dir == -1) || item.FlippedX) { spriteEffects |= SpriteEffects.FlipHorizontally; }
if (item.FlippedY) { spriteEffects |= SpriteEffects.FlipVertically; }
if ((item.body != null && item.body.Dir == -1) || item.FlippedX)
{
spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipVertically : SpriteEffects.FlipHorizontally;
}
if (item.FlippedY)
{
spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically;
}
int i = 0;
foreach (Item containedItem in Inventory.Items)
@@ -233,7 +255,7 @@ namespace Barotrauma.Items.Components
new Vector2(currentItemPos.X, -currentItemPos.Y),
containedItem.GetSpriteColor(),
origin,
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation),
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation + MathHelper.ToRadians(-item.Rotation)),
containedItem.Scale,
spriteEffects,
depth: containedSpriteDepth);
@@ -248,11 +270,11 @@ namespace Barotrauma.Items.Components
if (Math.Abs(ItemInterval.X) > 0.001f && Math.Abs(ItemInterval.Y) > 0.001f)
{
//interval set on both axes -> use a grid layout
currentItemPos.X += transformedItemInterval.X;
currentItemPos += transformedItemIntervalHorizontal;
if (i % ItemsPerRow == 0)
{
currentItemPos.X = transformedItemPos.X;
currentItemPos.Y += transformedItemInterval.Y;
currentItemPos = transformedItemPos;
currentItemPos += transformedItemIntervalVertical * (i / ItemsPerRow);
}
}
else
@@ -264,6 +286,7 @@ namespace Barotrauma.Items.Components
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
if (item.NonInteractable) { return; }
if (Inventory.RectTransform != null)
{
guiCustomComponent.RectTransform.Parent = Inventory.RectTransform;
@@ -272,7 +295,7 @@ namespace Barotrauma.Items.Components
//if the item is in the character's inventory, no need to update the item's inventory
//because the player can see it by hovering the cursor over the item
guiCustomComponent.Visible = item.ParentInventory?.Owner != character && DrawInventory;
if (!guiCustomComponent.Visible) return;
if (!guiCustomComponent.Visible) { return; }
Inventory.Update(deltaTime, cam);
}

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Text;
@@ -225,5 +226,10 @@ namespace Barotrauma.Items.Components
textBlock.TextOffset = drawPos - textBlock.Rect.Location.ToVector2() + new Vector2(scrollAmount + scrollPadding, 0.0f);
textBlock.DrawManually(spriteBatch);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
Text = msg.ReadString();
}
}
}

View File

@@ -49,8 +49,8 @@ namespace Barotrauma.Items.Components
if (light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn)
{
Vector2 origin = light.LightSprite.Origin;
if (light.LightSpriteEffect == SpriteEffects.FlipHorizontally) { origin.X = light.LightSprite.SourceRect.Width - origin.X; }
if (light.LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = light.LightSprite.SourceRect.Height - origin.Y; }
if ((light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = light.LightSprite.SourceRect.Width - origin.X; }
if ((light.LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y = light.LightSprite.SourceRect.Height - origin.Y; }
light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, origin, -light.Rotation, item.Scale, light.LightSpriteEffect, item.SpriteDepth - 0.0001f);
}
}

View File

@@ -52,7 +52,9 @@ namespace Barotrauma.Items.Components
RelativeOffset = new Vector2(0.05f, 0)
}, TextManager.Get("PumpAutoControl", fallBackTag: "ReactorAutoControl"), font: GUI.SubHeadingFont, style: "IndicatorLightYellow")
{
CanBeFocused = false
Selected = false,
Enabled = false,
ToolTip = TextManager.Get("AutoControlTip")
};
powerIndicator.TextBlock.Wrap = autoControlIndicator.TextBlock.Wrap = true;
powerIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor);
@@ -75,6 +77,7 @@ namespace Barotrauma.Items.Components
if (Math.Abs(newTargetForce - targetForce) < 0.01) { return false; }
targetForce = newTargetForce;
User = Character.Controlled;
if (GameMain.Client != null)
{
@@ -145,7 +148,7 @@ namespace Barotrauma.Items.Components
propellerSprite.Draw(spriteBatch, (int)Math.Floor(spriteIndex), drawPos, Color.White, propellerSprite.Origin, 0.0f, Vector2.One);
}
if (editing)
if (editing && !GUI.DisableHUD)
{
Vector2 drawPos = item.DrawPosition;
drawPos += PropellerPos;
@@ -164,11 +167,16 @@ namespace Barotrauma.Items.Components
{
if (correctionTimer > 0.0f)
{
StartDelayedCorrection(type, msg.ExtractBits(5), sendingTime);
StartDelayedCorrection(type, msg.ExtractBits(5 + 16), sendingTime);
return;
}
targetForce = msg.ReadRangedInteger(-10, 10) * 10.0f;
UInt16 userID = msg.ReadUInt16();
if (userID != Entity.NullEntityID)
{
User = Entity.FindEntityByID(userID) as Character;
}
}
}
}

View File

@@ -100,7 +100,7 @@ namespace Barotrauma.Items.Components
OnSelected = (component, userdata) =>
{
selectedItem = userdata as FabricationRecipe;
if (selectedItem != null) SelectItem(Character.Controlled, selectedItem);
if (selectedItem != null) { SelectItem(Character.Controlled, selectedItem); }
return true;
}
};
@@ -292,7 +292,7 @@ namespace Barotrauma.Items.Components
foreach (Item item in inputContainer.Inventory.Items)
{
if (item == null) { continue; }
missingItems.Remove(missingItems.FirstOrDefault(mi => mi.ItemPrefab == item.prefab));
missingItems.Remove(missingItems.FirstOrDefault(mi => mi.ItemPrefabs.Contains(item.prefab)));
}
var availableIngredients = GetAvailableIngredients();
@@ -328,12 +328,12 @@ namespace Barotrauma.Items.Components
if (slotIndex >= inputContainer.Capacity) { break; }
var itemIcon = requiredItem.ItemPrefab.InventoryIcon ?? requiredItem.ItemPrefab.sprite;
var itemIcon = requiredItem.ItemPrefabs.First().InventoryIcon ?? requiredItem.ItemPrefabs.First().sprite;
Rectangle slotRect = inputContainer.Inventory.slots[slotIndex].Rect;
itemIcon.Draw(
spriteBatch,
slotRect.Center.ToVector2(),
color: requiredItem.ItemPrefab.InventoryIconColor * 0.3f,
color: requiredItem.ItemPrefabs.First().InventoryIconColor * 0.3f,
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y));
if (requiredItem.UseCondition && requiredItem.MinCondition < 1.0f)
@@ -346,14 +346,16 @@ namespace Barotrauma.Items.Components
if (slotRect.Contains(PlayerInput.MousePosition))
{
string toolTipText = requiredItem.ItemPrefab.Name;
var suitableIngredients = requiredItem.ItemPrefabs.Select(ip => ip.Name);
string toolTipText = string.Join(", ", suitableIngredients.Count() > 3 ? suitableIngredients.SkipLast(suitableIngredients.Count() - 3) : suitableIngredients);
if (suitableIngredients.Count() > 3) { toolTipText += "..."; }
if (requiredItem.UseCondition && requiredItem.MinCondition < 1.0f)
{
toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%";
}
if (!string.IsNullOrEmpty(requiredItem.ItemPrefab.Description))
if (!string.IsNullOrEmpty(requiredItem.ItemPrefabs.First().Description))
{
toolTipText += '\n' + requiredItem.ItemPrefab.Description;
toolTipText += '\n' + requiredItem.ItemPrefabs.First().Description;
}
tooltip = new Pair<Rectangle, string>(slotRect, toolTipText);
}
@@ -378,10 +380,11 @@ namespace Barotrauma.Items.Components
if (fabricatedItem != null)
{
float clampedProgressState = Math.Clamp(progressState, 0f, 1f);
GUI.DrawRectangle(spriteBatch,
new Rectangle(
slotRect.X, slotRect.Y + (int)(slotRect.Height * (1.0f - progressState)),
slotRect.Width, (int)(slotRect.Height * progressState)),
slotRect.X, slotRect.Y + (int)(slotRect.Height * (1.0f - clampedProgressState)),
slotRect.Width, (int)(slotRect.Height * clampedProgressState)),
GUI.Style.Green * 0.5f, isFilled: true);
}
@@ -429,8 +432,10 @@ namespace Barotrauma.Items.Components
return true;
}
private bool SelectItem(Character user, FabricationRecipe selectedItem)
private bool SelectItem(Character user, FabricationRecipe selectedItem, float? overrideRequiredTime = null)
{
this.selectedItem = selectedItem;
selectedItemFrame.ClearChildren();
selectedItemReqsFrame.ClearChildren();
@@ -496,7 +501,8 @@ namespace Barotrauma.Items.Components
float degreeOfSuccess = user == null ? 0.0f : FabricationDegreeOfSuccess(user, selectedItem.RequiredSkills);
if (degreeOfSuccess > 0.5f) { degreeOfSuccess = 1.0f; }
float requiredTime = user == null ? selectedItem.RequiredTime : GetRequiredTime(selectedItem, user);
float requiredTime = overrideRequiredTime ??
(user == null ? selectedItem.RequiredTime : GetRequiredTime(selectedItem, user));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform),
TextManager.Get("FabricatorRequiredTime") , textColor: ToolBox.GradientLerp(degreeOfSuccess, GUI.Style.Red, Color.Yellow, GUI.Style.Green), font: GUI.SubHeadingFont)
@@ -605,7 +611,7 @@ namespace Barotrauma.Items.Components
State = newState;
timeUntilReady = newTimeUntilReady;
if (newState == FabricatorState.Stopped || itemIndex == -1 || user == null)
if (newState == FabricatorState.Stopped || itemIndex == -1)
{
CancelFabricating();
}

View File

@@ -81,7 +81,9 @@ namespace Barotrauma.Items.Components
autoControlIndicator = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.25f), rightArea.RectTransform, Anchor.TopLeft),
TextManager.Get("PumpAutoControl", fallBackTag: "ReactorAutoControl"), font: GUI.SubHeadingFont, style: "IndicatorLightYellow")
{
CanBeFocused = false
Selected = false,
Enabled = false,
ToolTip = TextManager.Get("AutoControlTip")
};
autoControlIndicator.TextBlock.AutoScaleHorizontal = true;
autoControlIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor);

View File

@@ -131,17 +131,23 @@ namespace Barotrauma.Items.Components
criticalHeatWarning = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
TextManager.Get("ReactorWarningCriticalTemp"), font: GUI.SubHeadingFont, style: "IndicatorLightRed")
{
CanBeFocused = false
Selected = false,
Enabled = false,
ToolTip = TextManager.Get("ReactorHeatTip")
};
lowTemperatureWarning = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
TextManager.Get("ReactorWarningCriticalLowTemp"), font: GUI.SubHeadingFont, style: "IndicatorLightRed")
{
CanBeFocused = false
Selected = false,
Enabled = false,
ToolTip = TextManager.Get("ReactorTempTip")
};
criticalOutputWarning = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
TextManager.Get("ReactorWarningCriticalOutput"), font: GUI.SubHeadingFont, style: "IndicatorLightRed")
{
CanBeFocused = false
Selected = false,
Enabled = false,
ToolTip = TextManager.Get("ReactorOutputTip")
};
List<GUITickBox> indicatorLights = new List<GUITickBox>() { criticalHeatWarning, lowTemperatureWarning, criticalOutputWarning };
indicatorLights.ForEach(l => l.TextBlock.OverrideTextColor(GUI.Style.TextColor));

View File

@@ -6,7 +6,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma.Items.Components
{
@@ -20,7 +19,7 @@ namespace Barotrauma.Items.Components
private PathFinder pathFinder;
private bool dynamicDockingIndicator = true;
private readonly bool dynamicDockingIndicator = true;
private bool unsentChanges;
private float networkUpdateTimer;
@@ -378,7 +377,6 @@ namespace Barotrauma.Items.Components
float edgeDist = Rand.Range(0.0f, 1.0f);
Vector2 blipPos = trigger.WorldPosition + Rand.Vector(trigger.ColliderRadius * edgeDist);
Vector2 blipVel = flow;
if (trigger.ForceFalloff) flow *= (1.0f - edgeDist);
//go through other triggers in range and add the flows of the ones that the blip is inside
foreach (KeyValuePair<LevelTrigger, Vector2> triggerFlow2 in levelTriggerFlows)
@@ -387,7 +385,7 @@ namespace Barotrauma.Items.Components
if (trigger2 != trigger && Vector2.DistanceSquared(blipPos, trigger2.WorldPosition) < trigger2.ColliderRadius * trigger2.ColliderRadius)
{
Vector2 trigger2flow = triggerFlow2.Value;
if (trigger2.ForceFalloff) trigger2flow *= (1.0f - Vector2.Distance(blipPos, trigger2.WorldPosition) / trigger2.ColliderRadius);
if (trigger2.ForceFalloff) trigger2flow *= 1.0f - Vector2.Distance(blipPos, trigger2.WorldPosition) / trigger2.ColliderRadius;
blipVel += trigger2flow;
}
}
@@ -507,7 +505,7 @@ namespace Barotrauma.Items.Components
float passivePingRadius = (float)(Timing.TotalTime % 1.0f);
if (passivePingRadius > 0.0f)
{
disruptedDirections.Clear();
if (activePingsCount == 0) { disruptedDirections.Clear(); }
foreach (AITarget t in AITarget.List)
{
if (t.Entity is Character c && c.Params.HideInSonar) { continue; }
@@ -581,14 +579,14 @@ namespace Barotrauma.Items.Components
}
}
if (currentMode == Mode.Active && currentPingIndex != -1)
if (currentPingIndex != -1)
{
var activePing = activePings[currentPingIndex];
if (activePing.IsDirectional && directionalPingCircle != null)
{
directionalPingCircle.Draw(spriteBatch, center, Color.White * (1.0f - activePing.State),
rotate: MathUtils.VectorToAngle(activePing.Direction),
scale: (DisplayRadius / directionalPingCircle.size.X) * activePing.State);
rotate: MathUtils.VectorToAngle(activePing.Direction),
scale: DisplayRadius / directionalPingCircle.size.X * activePing.State);
}
else
{
@@ -611,13 +609,13 @@ namespace Barotrauma.Items.Components
if (sonarBlips.Count > 0)
{
zoomSqrt = (float)Math.Sqrt(zoom);
float blipScale = 0.08f * (float)Math.Sqrt(zoom) * (rect.Width / 700.0f);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive);
foreach (SonarBlip sonarBlip in sonarBlips)
{
DrawBlip(spriteBatch, sonarBlip, transducerCenter, center, sonarBlip.FadeTimer / 2.0f * signalStrength);
DrawBlip(spriteBatch, sonarBlip, transducerCenter, center, sonarBlip.FadeTimer / 2.0f * signalStrength, blipScale);
}
spriteBatch.End();
@@ -1297,7 +1295,7 @@ namespace Barotrauma.Items.Components
return true;
}
private void DrawBlip(SpriteBatch spriteBatch, SonarBlip blip, Vector2 transducerPos, Vector2 center, float strength)
private void DrawBlip(SpriteBatch spriteBatch, SonarBlip blip, Vector2 transducerPos, Vector2 center, float strength, float blipScale)
{
strength = MathHelper.Clamp(strength, 0.0f, 1.0f);
@@ -1306,8 +1304,8 @@ namespace Barotrauma.Items.Components
Vector2 pos = (blip.Position - transducerPos) * displayScale * zoom;
pos.Y = -pos.Y;
if (Rand.Range(0.5f, 2.0f) < distort) pos.X = -pos.X;
if (Rand.Range(0.5f, 2.0f) < distort) pos.Y = -pos.Y;
if (Rand.Range(0.5f, 2.0f) < distort) { pos.X = -pos.X; }
if (Rand.Range(0.5f, 2.0f) < distort) { pos.Y = -pos.Y; }
float posDistSqr = pos.LengthSquared();
if (posDistSqr > DisplayRadius * DisplayRadius)
@@ -1324,15 +1322,15 @@ namespace Barotrauma.Items.Components
Vector2 dir = pos / (float)Math.Sqrt(posDistSqr);
Vector2 normal = new Vector2(dir.Y, -dir.X);
float scale = (strength + 3.0f) * blip.Scale * zoomSqrt;
float scale = (strength + 3.0f) * blip.Scale * blipScale;
Color color = ToolBox.GradientLerp(strength, blipColorGradient[blip.BlipType]);
sonarBlip.Draw(spriteBatch, center + pos, color, sonarBlip.Origin, blip.Rotation ?? MathUtils.VectorToAngle(pos),
blip.Size * scale * 0.04f, SpriteEffects.None, 0);
blip.Size * scale * 0.5f, SpriteEffects.None, 0);
pos += Rand.Range(0.0f, 1.0f) * dir + Rand.Range(-scale, scale) * normal;
sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f, sonarBlip.Origin, 0, scale * 0.08f, SpriteEffects.None, 0);
sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f, sonarBlip.Origin, 0, scale, SpriteEffects.None, 0);
}
private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius)

View File

@@ -22,6 +22,7 @@ namespace Barotrauma.Items.Components
LevelEnd,
LevelStart
};
private GUITickBox maintainPosTickBox, levelEndTickBox, levelStartTickBox;
private GUIComponent statusContainer, dockingContainer, controlContainer;
@@ -349,20 +350,34 @@ namespace Barotrauma.Items.Components
{
OnClicked = (btn, userdata) =>
{
if (GameMain.GameSession?.Campaign != null)
if (GameMain.GameSession?.Campaign is CampaignMode campaign)
{
if (Level.IsLoadedOutpost &&
DockingSources.Any(d => d.Docked && (d.DockingTarget?.Item.Submarine?.Info?.IsOutpost ?? false)))
{
GameMain.GameSession.Campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
GameMain.GameSession.Campaign.ShowCampaignUI = true;
// Undocking from an outpost
campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
campaign.ShowCampaignUI = true;
return false;
}
else if (!Level.IsLoadedOutpost && DockingModeEnabled && ActiveDockingSource != null &&
!ActiveDockingSource.Docked && (DockingTarget?.Item?.Submarine?.Info.IsOutpost ?? false))
!ActiveDockingSource.Docked && DockingTarget?.Item?.Submarine == Level.Loaded.StartOutpost && (DockingTarget?.Item?.Submarine?.Info.IsOutpost ?? false))
{
enterOutpostPrompt = new GUIMessageBox("", TextManager.GetWithVariable("campaignenteroutpostprompt", "[locationname]", DockingTarget.Item.Submarine.Info.Name), new string[] { TextManager.Get("yes"), TextManager.Get("no") });
// Docking to an outpost
var subsToLeaveBehind = campaign.GetSubsToLeaveBehind(Item.Submarine);
if (subsToLeaveBehind.Any())
{
enterOutpostPrompt = new GUIMessageBox(
TextManager.GetWithVariable("enterlocation", "[locationname]", DockingTarget.Item.Submarine.Info.Name),
TextManager.Get(subsToLeaveBehind.Count == 1 ? "LeaveSubBehind" : "LeaveSubsBehind"),
new string[] { TextManager.Get("yes"), TextManager.Get("no") });
}
else
{
enterOutpostPrompt = new GUIMessageBox("",
TextManager.GetWithVariable("campaignenteroutpostprompt", "[locationname]", DockingTarget.Item.Submarine.Info.Name),
new string[] { TextManager.Get("yes"), TextManager.Get("no") });
}
enterOutpostPrompt.Buttons[0].OnClicked += (btn, userdata) =>
{
SendDockingSignal();
@@ -780,10 +795,16 @@ namespace Barotrauma.Items.Components
}
checkConnectedPortsTimer = CheckConnectedPortsInterval;
}
else
{
checkConnectedPortsTimer -= deltaTime;
}
float closestDist = DockingAssistThreshold * DockingAssistThreshold;
DockingModeEnabled = false;
if (connectedPorts.None()) { return; }
float closestDist = DockingAssistThreshold * DockingAssistThreshold;
foreach (DockingPort sourcePort in connectedPorts)
{
if (sourcePort.Docked || sourcePort.Item.Submarine == null) { continue; }
@@ -795,15 +816,14 @@ namespace Barotrauma.Items.Components
{
if (targetPort.Docked || targetPort.Item.Submarine == null) { continue; }
if (targetPort.Item.Submarine == controlledSub || targetPort.IsHorizontal != sourcePort.IsHorizontal) { continue; }
if (targetPort.Item.Submarine.DockedTo?.Contains(sourcePort.Item.Submarine) ?? false) { continue; }
if (Level.Loaded != null && targetPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
int targetDir = targetPort.GetDir();
if (sourceDir == targetDir) { continue; }
if (sourceDir == targetPort.GetDir()) { continue; }
float dist = Vector2.DistanceSquared(sourcePort.Item.WorldPosition, targetPort.Item.WorldPosition);
if (dist < closestDist)
{
closestDist = dist;
DockingModeEnabled = true;
ActiveDockingSource = sourcePort;
DockingTarget = targetPort;

View File

@@ -0,0 +1,55 @@
using System;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
{
internal partial class Planter
{
public Vector2 DrawSize => CalculateSize();
private Vector2 CalculateSize()
{
if (GrowableSeeds.All(s => s == null)) { return Vector2.Zero; }
Point pos = item.DrawPosition.ToPoint();
Rectangle rect = new Rectangle(pos, Point.Zero);
for (int i = 0; i < GrowableSeeds.Length; i++)
{
Growable seed = GrowableSeeds[i];
PlantSlot slot = PlantSlots.ContainsKey(i) ? PlantSlots[i] : NullSlot;
if (seed == null) { continue; }
foreach (VineTile vine in seed.Vines)
{
Rectangle worldRect = vine.Rect;
worldRect.Location += slot.Offset.ToPoint();
worldRect.Location += pos;
rect = Rectangle.Union(rect, worldRect);
}
}
Vector2 result = new Vector2(MaxDistance(pos.X, rect.Left, rect.Right) * 2, MaxDistance(pos.Y, rect.Top, rect.Bottom) * 2);
return result;
static float MaxDistance(float origin, float x, float y)
{
return Math.Max(Math.Abs(origin - x), Math.Abs(origin - y));
}
}
private LightComponent lightComponent;
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
for (var i = 0; i < GrowableSeeds.Length; i++)
{
Growable growable = GrowableSeeds[i];
PlantSlot slot = PlantSlots.ContainsKey(i) ? PlantSlots[i] : NullSlot;
growable?.Draw(spriteBatch, this, slot.Offset, itemDepth);
}
}
}
}

View File

@@ -29,6 +29,7 @@ namespace Barotrauma.Items.Components
private List<Pair<RelatedItem, ParticleEmitter>> particleEmitterHitItem = new List<Pair<RelatedItem, ParticleEmitter>>();
private float prevProgressBarState;
private Item prevProgressBarTarget = null;
partial void InitProjSpecific(XElement element)
{
@@ -65,7 +66,7 @@ namespace Barotrauma.Items.Components
{
foreach (ParticleEmitter particleEmitter in particleEmitters)
{
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
particleEmitter.Emit(
deltaTime, ConvertUnits.ToDisplayUnits(raystart),
item.CurrentHull, particleAngle, particleEmitter.Prefab.CopyEntityAngle ? -particleAngle : 0);
@@ -92,7 +93,7 @@ namespace Barotrauma.Items.Components
if (targetStructure.Submarine != null) particlePos += targetStructure.Submarine.DrawPosition;
foreach (var emitter in particleEmitterHitStructure)
{
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
emitter.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
}
}
@@ -103,7 +104,7 @@ namespace Barotrauma.Items.Components
if (targetCharacter.Submarine != null) particlePos += targetCharacter.Submarine.DrawPosition;
foreach (var emitter in particleEmitterHitCharacter)
{
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
emitter.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
}
}
@@ -111,7 +112,7 @@ namespace Barotrauma.Items.Components
partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem)
{
float progressBarState = targetItem.ConditionPercentage / 100.0f;
if (!MathUtils.NearlyEqual(progressBarState, prevProgressBarState))
if (!MathUtils.NearlyEqual(progressBarState, prevProgressBarState) || prevProgressBarTarget != targetItem)
{
var door = targetItem.GetComponent<Door>();
if (door == null || door.Stuck <= 0)
@@ -121,18 +122,20 @@ namespace Barotrauma.Items.Components
targetItem,
progressBarPos,
progressBarState,
GUI.Style.Red, GUI.Style.Green);
GUI.Style.Red, GUI.Style.Green,
progressBarState < prevProgressBarState ? "progressbar.cutting" : "");
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
}
prevProgressBarState = progressBarState;
prevProgressBarTarget = targetItem;
}
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
if (targetItem.Submarine != null) particlePos += targetItem.Submarine.DrawPosition;
foreach (var emitter in particleEmitterHitItem)
{
if (!emitter.First.MatchesItem(targetItem)) continue;
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
if (!emitter.First.MatchesItem(targetItem)) { continue; }
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
emitter.Second.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
}
}

View File

@@ -192,6 +192,7 @@ namespace Barotrauma.Items.Components
foreach (Wire wire in panel.DisconnectedWires)
{
if (wire == DraggingConnected && mouseInRect) { continue; }
if (wire.HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; }
Connection recipient = wire.OtherConnection(null);
string label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
@@ -238,6 +239,7 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < MaxLinked; i++)
{
if (wires[i] == null || wires[i].Hidden || (DraggingConnected == wires[i] && (mouseIn || Screen.Selected == GameMain.SubEditorScreen))) { continue; }
if (wires[i].HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; }
Connection recipient = wires[i].OtherConnection(this);
string label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
@@ -289,7 +291,7 @@ namespace Barotrauma.Items.Components
flashColor * (float)Math.Sin(FlashTimer % flashCycleDuration / flashCycleDuration * MathHelper.Pi * 0.8f), scale: connectorSpriteScale);
}
if (Wires.Any(w => w != null && w != DraggingConnected && !w.Hidden))
if (Wires.Any(w => w != null && w != DraggingConnected && !w.Hidden && (!w.HiddenInGame || Screen.Selected != GameMain.GameScreen)))
{
int screwIndex = (int)Math.Floor(position.Y / 30.0f) % screwSprites.Count;
screwSprites[screwIndex].Draw(spriteBatch, position, scale: connectorSpriteScale);
@@ -325,7 +327,7 @@ namespace Barotrauma.Items.Components
canDrag &&
((PlayerInput.MousePosition.X > Math.Min(start.X, end.X) &&
PlayerInput.MousePosition.X < Math.Max(start.X, end.X) &&
MathUtils.LineToPointDistance(start, end, PlayerInput.MousePosition) < 6) ||
MathUtils.LineToPointDistanceSquared(start, end, PlayerInput.MousePosition) < 36) ||
Vector2.Distance(end, PlayerInput.MousePosition) < 20.0f ||
new Rectangle((start.X < end.X) ? textX - 100 : textX, (int)start.Y - 5, 100, 14).Contains(PlayerInput.MousePosition));

View File

@@ -0,0 +1,12 @@
using Barotrauma.Networking;
namespace Barotrauma.Items.Components
{
partial class MemoryComponent : ItemComponent
{
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
Value = msg.ReadString();
}
}
}

View File

@@ -165,6 +165,12 @@ namespace Barotrauma.Items.Components
}
else
{
if (!string.IsNullOrEmpty(target.customInteractHUDText) && target.AllowCustomInteract)
{
texts.Add(target.customInteractHUDText);
textColors.Add(GUI.Style.Green);
}
if (target.IsUnconscious)
{
texts.Add(TextManager.Get("Unconscious"));

View File

@@ -17,6 +17,17 @@ namespace Barotrauma.Items.Components
private GUIProgressBar powerIndicator;
public int UIElementHeight
{
get
{
int height = 0;
if (ShowChargeIndicator) { height += powerIndicator.Rect.Height; }
if (ShowProjectileIndicator) { height += (int)(Inventory.SlotSpriteSmall.size.Y * Inventory.UIScale) + 5; }
return height;
}
}
private float recoilTimer;
private float RetractionTime => Math.Max(Reload * RetractionDurationMultiplier, RecoilTime);
@@ -235,12 +246,11 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{
Vector2 drawPos = new Vector2(item.Rect.X + transformedBarrelPos.X, item.Rect.Y - transformedBarrelPos.Y);
if (item.Submarine != null)
if (!MathUtils.NearlyEqual(item.Rotation, prevBaseRotation))
{
drawPos += item.Submarine.DrawPosition;
UpdateTransformedBarrelPos();
}
drawPos.Y = -drawPos.Y;
Vector2 drawPos = GetDrawPos();
float recoilOffset = 0.0f;
if (Math.Abs(RecoilDistance) > 0.0f && recoilTimer > 0.0f)
@@ -275,7 +285,7 @@ namespace Barotrauma.Items.Components
rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
if (!editing) { return; }
if (!editing || GUI.DisableHUD) { return; }
float widgetRadius = 60.0f;
@@ -310,7 +320,7 @@ namespace Barotrauma.Items.Components
};
widget.MouseHeld += (deltaTime) =>
{
minRotation = GetRotationAngle(drawPos);
minRotation = GetRotationAngle(GetDrawPos());
if (minRotation > maxRotation)
{
float temp = minRotation;
@@ -332,7 +342,7 @@ namespace Barotrauma.Items.Components
widget.PreDraw += (sprtBtch, deltaTime) =>
{
widget.tooltip = "Min: " + (int)MathHelper.ToDegrees(minRotation);
widget.DrawPos = drawPos + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * widgetRadius;
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * widgetRadius;
widget.Update(deltaTime);
};
});
@@ -351,7 +361,7 @@ namespace Barotrauma.Items.Components
};
widget.MouseHeld += (deltaTime) =>
{
maxRotation = GetRotationAngle(drawPos);
maxRotation = GetRotationAngle(GetDrawPos());
if (minRotation > maxRotation)
{
float temp = minRotation;
@@ -373,12 +383,20 @@ namespace Barotrauma.Items.Components
widget.PreDraw += (sprtBtch, deltaTime) =>
{
widget.tooltip = "Max: " + (int)MathHelper.ToDegrees(maxRotation);
widget.DrawPos = drawPos + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * widgetRadius;
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * widgetRadius;
widget.Update(deltaTime);
};
});
minRotationWidget.Draw(spriteBatch, (float)Timing.Step);
maxRotationWidget.Draw(spriteBatch, (float)Timing.Step);
Vector2 GetDrawPos()
{
Vector2 drawPos = new Vector2(item.Rect.X + transformedBarrelPos.X, item.Rect.Y - transformedBarrelPos.Y);
if (item.Submarine != null) { drawPos += item.Submarine.DrawPosition; }
drawPos.Y = -drawPos.Y;
return drawPos;
}
}
private Widget GetWidget(string id, SpriteBatch spriteBatch, int size = 5, Action<Widget> initMethod = null)

View File

@@ -715,7 +715,12 @@ namespace Barotrauma
/// <returns></returns>
public static bool IsMouseOnInventory(bool ignoreDraggedItem = false)
{
var isSubEditor = Screen.Selected is SubEditorScreen editor && !editor.WiringMode;
if (GameMain.GameSession?.Campaign != null &&
(GameMain.GameSession.Campaign.ShowCampaignUI || GameMain.GameSession.Campaign.ForceMapUI))
{
return false;
}
if (Character.Controlled == null) { return false; }
if (!ignoreDraggedItem)
@@ -723,6 +728,8 @@ namespace Barotrauma
if (draggingItem != null || DraggingInventory != null) { return true; }
}
var isSubEditor = Screen.Selected is SubEditorScreen editor && !editor.WiringMode;
if (Character.Controlled.Inventory != null && !isSubEditor)
{
var inv = Character.Controlled.Inventory;
@@ -840,7 +847,8 @@ namespace Barotrauma
foreach (var ic in character.SelectedConstruction.ActiveHUDs)
{
var itemContainer = ic as ItemContainer;
if (itemContainer?.Inventory?.slots == null) continue;
if (itemContainer?.Inventory?.slots == null) { continue; }
if (ic.Item.NonInteractable) { continue; }
foreach (var slot in itemContainer.Inventory.slots)
{
@@ -1127,7 +1135,6 @@ namespace Barotrauma
return hoverArea;
}
public static void DrawFront(SpriteBatch spriteBatch)
{
if (GUI.PauseMenuOpen || GUI.SettingsMenuOpen) { return; }
@@ -1202,6 +1209,7 @@ namespace Barotrauma
}
Color slotColor = Color.White;
if ((inventory?.Owner as Item)?.NonInteractable ?? false) { slotColor = Color.Gray; }
var itemContainer = item?.GetComponent<ItemContainer>();
if (itemContainer != null && (itemContainer.InventoryTopSprite != null || itemContainer.InventoryBottomSprite != null))
{
@@ -1470,18 +1478,19 @@ namespace Barotrauma
{
if (receivedItemIDs[i] == 0 || (Entity.FindEntityByID(receivedItemIDs[i]) as Item != Items[i]))
{
if (Items[i] != null) Items[i].Drop(null);
Items[i]?.Drop(null);
System.Diagnostics.Debug.Assert(Items[i] == null);
}
}
for (int i = 0; i < capacity; i++)
//iterate backwards to get the item to the Any slots first
for (int i = capacity - 1; i >= 0; i--)
{
if (receivedItemIDs[i] > 0)
{
if (!(Entity.FindEntityByID(receivedItemIDs[i]) is Item item) || Items[i] == item) { continue; }
TryPutItem(item, i, true, true, null, false);
TryPutItem(item, i, false, false, null, false);
for (int j = 0; j < capacity; j++)
{
if (Items[j] == item && receivedItemIDs[j] != item.ID)

View File

@@ -92,8 +92,6 @@ namespace Barotrauma
return parentInventory == null && (body == null || body.Enabled) && ShowItems;
}
}
public float SpriteRotation;
public Color GetSpriteColor()
{
@@ -297,7 +295,7 @@ namespace Barotrauma
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
decorativeSprite.Sprite.DrawTiled(spriteBatch,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
size, color: color,
@@ -307,15 +305,24 @@ namespace Barotrauma
}
else
{
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, SpriteRotation + rotation, Scale, activeSprite.effects, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, SpriteRotation + rotation, Scale, activeSprite.effects, depth - 0.000001f);
Vector2 origin = activeSprite.Origin;
if ((activeSprite.effects & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally)
{
origin.X = activeSprite.SourceRect.Width - origin.X;
}
if ((activeSprite.effects & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically)
{
origin.Y = activeSprite.SourceRect.Height - origin.Y;
}
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f);
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
SpriteRotation + rotation + rot, decorativeSprite.Scale * Scale, activeSprite.effects,
rotationRad + rot, decorativeSprite.Scale * Scale, activeSprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
}
}
@@ -358,7 +365,7 @@ namespace Barotrauma
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
var ca = (float)Math.Cos(-body.Rotation);
var sa = (float)Math.Sin(-body.Rotation);
@@ -378,7 +385,7 @@ namespace Barotrauma
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
var (xOff, yOff) = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
var (xOff, yOff) = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + xOff, -(DrawPosition.Y + yOff)), color,
rotation, decorativeSprite.Scale * Scale, activeSprite.effects,
@@ -437,7 +444,7 @@ namespace Barotrauma
}
}
if (!ShowLinks) return;
if (!ShowLinks || GUI.DisableHUD) { return; }
foreach (MapEntity e in linkedTo)
{
@@ -455,21 +462,6 @@ namespace Barotrauma
}
}
private void DrawDecorativeSprite(SpriteBatch spriteBatch, DecorativeSprite decorativeSprite, Color color, float depth)
{
if (!spriteAnimState[decorativeSprite].IsActive) { return; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
var ca = (float)Math.Cos(-body.Rotation);
var sa = (float)Math.Sin(-body.Rotation);
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + transformedOffset.X, -(DrawPosition.Y + transformedOffset.Y)), color,
-body.Rotation + rotation, decorativeSprite.Scale * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
partial void OnCollisionProjSpecific(float impact)
{
if (impact > 1.0f &&
@@ -512,7 +504,7 @@ namespace Barotrauma
}
if (Screen.Selected != GameMain.SubEditorScreen) { return; }
if (Character.Controlled == null) { activeHUDs.Clear(); }
if (!Linkable) { return; }
@@ -1154,7 +1146,6 @@ namespace Barotrauma
editingHUDRefreshPending = true;
break;
case NetEntityEvent.Type.Upgrade:
{
string identifier = msg.ReadString();
byte level = msg.ReadByte();
if (UpgradePrefab.Find(identifier) is { } upgradePrefab)
@@ -1174,8 +1165,7 @@ namespace Barotrauma
AddUpgrade(upgrade, false);
}
break;
}
break;
case NetEntityEvent.Type.Invalid:
break;
}
@@ -1391,7 +1381,7 @@ namespace Barotrauma
if (itemPrefab == null)
{
string errorMsg = "Failed to spawn item, prefab not found (name: " + (itemName ?? "null") + ", identifier: " + (itemIdentifier ?? "null") + ")";
errorMsg += "\n" + string.Join(", ", GameMain.Config.SelectedContentPackages.Select(cp => cp.Name));
errorMsg += "\n" + string.Join(", ", GameMain.Config.AllEnabledPackages.Select(cp => cp.Name));
GameAnalyticsManager.AddErrorEventOnce("Item.ReadSpawnData:PrefabNotFound" + (itemName ?? "null") + (itemIdentifier ?? "null"),
GameAnalyticsSDK.Net.EGAErrorSeverity.Critical,
errorMsg);

View File

@@ -55,6 +55,7 @@ namespace Barotrauma
public List<ContainedItemSprite> ContainedSprites = new List<ContainedItemSprite>();
public Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
public Sprite InventoryIcon;
public Sprite MinimapIcon;
//only used to display correct color in the sub editor, item instances have their own property that can be edited on a per-item basis
[Serialize("1.0,1.0,1.0,1.0", false)]
@@ -90,6 +91,7 @@ namespace Barotrauma
};
item.SetTransform(ConvertUnits.ToSimUnits(Submarine.MainSub == null ? item.Position : item.Position - Submarine.MainSub.Position), 0.0f);
item.FindHull();
item.Submarine = Submarine.MainSub;
if (PlayerInput.IsShiftDown())
{

View File

@@ -14,7 +14,7 @@ namespace Barotrauma
Vector2.Zero, 0.0f, hull);
}
hull = hull ?? Hull.FindHull(worldPosition, useWorldCoordinates: true);
hull ??= Hull.FindHull(worldPosition, useWorldCoordinates: true);
bool underwater = hull == null || worldPosition.Y < hull.WorldSurface;
if (underwater && underwaterBubble)
@@ -44,7 +44,7 @@ namespace Barotrauma
}
if (smoke)
{
var smokeParticle = GameMain.ParticleManager.CreateParticle(Rand.Range(0.0f, 1.0f) < 0.5f ? "explosionsmoke" : "smoke",
GameMain.ParticleManager.CreateParticle(Rand.Range(0.0f, 1.0f) < 0.5f ? "explosionsmoke" : "smoke",
ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, attack.Range))), hull),
Rand.Vector(Rand.Range(0.0f, particleSpeed)), 0.0f, hull);
}
@@ -75,11 +75,6 @@ namespace Barotrauma
}
}
if (hull != null && !string.IsNullOrWhiteSpace(decal) && decalSize > 0.0f)
{
hull.AddDecal(decal, worldPosition, decalSize);
}
if (flash)
{
float displayRange = flashRange.HasValue ? flashRange.Value : attack.Range;

View File

@@ -12,35 +12,10 @@ namespace Barotrauma
partial void UpdateProjSpecific(float growModifier)
{
EmitParticles(size, WorldPosition, hull, growModifier, OnChangeHull);
lightSource.Color = new Color(1.0f, 0.45f, 0.3f) * Rand.Range(0.8f, 1.0f);
if (Math.Abs((lightSource.Range * 0.2f) - Math.Max(size.X, size.Y)) > 1.0f) lightSource.Range = Math.Max(size.X, size.Y) * 5.0f;
if (Vector2.DistanceSquared(lightSource.Position,position) > 5.0f) lightSource.Position = position + Vector2.UnitY * 30.0f;
if (size.X > 256.0f)
{
if (burnDecals.Count == 0)
{
var newDecal = hull.AddDecal("burnt", WorldPosition + size/2);
if (newDecal != null) burnDecals.Add(newDecal);
}
else if (WorldPosition.X < burnDecals[0].WorldPosition.X - 256.0f)
{
var newDecal = hull.AddDecal("burnt", WorldPosition);
if (newDecal != null) burnDecals.Insert(0, newDecal);
}
else if (WorldPosition.X + size.X > burnDecals[burnDecals.Count-1].WorldPosition.X + 256.0f)
{
var newDecal = hull.AddDecal("burnt", WorldPosition + Vector2.UnitX * size.X);
if (newDecal != null) burnDecals.Add(newDecal);
}
}
foreach (Decal d in burnDecals)
{
//prevent the decals from fading out as long as the firesource is alive
d.FadeTimer = Math.Min(d.FadeTimer, d.FadeInTime);
}
if (Vector2.DistanceSquared(lightSource.Position, position) > 5.0f) lightSource.Position = position + Vector2.UnitY * 30.0f;
}
public static void EmitParticles(Vector2 size, Vector2 worldPosition, Hull hull, float growModifier, Particle.OnChangeHullHandler onChangeHull = null)

View File

@@ -1,6 +1,4 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
@@ -12,16 +10,10 @@ namespace Barotrauma
{
partial class Hull : MapEntity, ISerializableEntity, IServerSerializable, IClientSerializable
{
public const int MaxDecalsPerHull = 10;
private readonly List<Decal> decals = new List<Decal>();
private float serverUpdateDelay;
private float remoteWaterVolume, remoteOxygenPercentage;
private List<Vector3> remoteFireSources;
private bool networkUpdatePending;
private float networkUpdateTimer;
private readonly List<BackgroundSection> remoteBackgroundSections = new List<BackgroundSection>();
private double lastAmbientLightEditTime;
@@ -49,11 +41,14 @@ namespace Barotrauma
}
}
private float paintAmount = 0.0f;
private float minimumPaintAmountToDraw;
public override bool IsVisible(Rectangle worldView)
{
if (Screen.Selected != GameMain.SubEditorScreen && !GameMain.DebugDraw)
{
if (decals.Count == 0) { return false; }
if (decals.Count == 0 && paintAmount < minimumPaintAmountToDraw) { return false; }
Rectangle worldRect = WorldRect;
if (worldRect.X > worldView.Right || worldRect.Right < worldView.X) { return false; }
@@ -70,20 +65,6 @@ namespace Barotrauma
!Submarine.RectContains(MathUtils.ExpandRect(WorldRect, -8), position));
}
public Decal AddDecal(string decalName, Vector2 worldPosition, float scale = 1.0f)
{
if (decals.Count >= MaxDecalsPerHull) return null;
var decal = GameMain.DecalManager.CreateDecal(decalName, scale, worldPosition, this);
if (decal != null)
{
decals.Add(decal);
}
return decal;
}
private GUIComponent CreateEditingHUD(bool inGame = false)
{
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this };
@@ -101,39 +82,32 @@ namespace Barotrauma
{
editingHUD = CreateEditingHUD(Screen.Selected != GameMain.SubEditorScreen);
}
if (!PlayerInput.KeyDown(Keys.Space)) return;
if (!PlayerInput.KeyDown(Keys.Space)) { return; }
bool lClick = PlayerInput.PrimaryMouseButtonClicked();
bool rClick = PlayerInput.SecondaryMouseButtonClicked();
if (!lClick && !rClick) return;
if (!lClick && !rClick) { return; }
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
if (lClick)
foreach (MapEntity entity in mapEntityList)
{
foreach (MapEntity entity in mapEntityList)
if (entity == this || !entity.IsHighlighted) { continue; }
if (!entity.IsMouseOn(position)) { continue; }
if (entity.linkedTo != null && entity.linkedTo.Contains(this))
{
if (entity == this || !entity.IsHighlighted) continue;
if (!entity.IsMouseOn(position)) continue;
if (entity.Linkable && entity.linkedTo != null)
if (entity.Linkable && entity.linkedTo != null && !entity.linkedTo.Contains(this))
{
entity.linkedTo.Add(this);
linkedTo.Add(entity);
}
}
}
else
{
foreach (MapEntity entity in mapEntityList)
else if (entity.Linkable && entity.linkedTo != null)
{
if (entity == this || !entity.IsHighlighted) continue;
if (!entity.IsMouseOn(position)) continue;
if (entity.linkedTo != null && entity.linkedTo.Contains(this))
{
entity.linkedTo.Remove(this);
linkedTo.Remove(entity);
}
entity.linkedTo.Add(this);
linkedTo.Add(entity);
}
}
}
@@ -151,7 +125,15 @@ namespace Barotrauma
networkUpdateTimer += deltaTime;
if (networkUpdateTimer > 0.2f)
{
GameMain.NetworkMember?.CreateEntityEvent(this);
if (!pendingSectionUpdates.Any())
{
GameMain.NetworkMember?.CreateEntityEvent(this);
}
foreach (int pendingSectionUpdate in pendingSectionUpdates)
{
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { pendingSectionUpdate });
}
pendingSectionUpdates.Clear();
networkUpdatePending = false;
networkUpdateTimer = 0.0f;
}
@@ -189,14 +171,7 @@ namespace Barotrauma
}
}
}
foreach (Decal decal in decals)
{
decal.Update(deltaTime);
}
decals.RemoveAll(d => d.FadeTimer >= d.LifeTime);
if (waterVolume < 1.0f) return;
for (int i = 1; i < waveY.Length - 1; i++)
{
@@ -324,6 +299,35 @@ namespace Barotrauma
}
}
public void DrawSectionColors(SpriteBatch spriteBatch)
{
Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
Point sectionSize = BackgroundSections[0].Rect.Size;
Vector2 drawPos = drawOffset + new Vector2(rect.Location.X + sectionSize.X / 2, rect.Location.Y - sectionSize.Y / 2);
for (int i = 0; i < BackgroundSections.Count; i++)
{
BackgroundSection section = BackgroundSections[i];
if (section.ColorStrength < 0.01f || section.Color.A < 1) { continue; }
if (GameMain.DecalManager.GrimeSprites.Count == 0)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(drawOffset.X + rect.X + section.Rect.X, -(drawOffset.Y + rect.Y + section.Rect.Y)),
new Vector2(sectionSize.X, sectionSize.Y),
section.GetStrengthAdjustedColor(), true, 0.0f, (int)Math.Max(1.5f / Screen.Selected.Cam.Zoom, 1.0f));
}
else
{
Vector2 sectionPos = new Vector2(drawPos.X + section.Rect.Location.X, -(drawPos.Y + section.Rect.Location.Y));
Vector2 randomOffset = new Vector2(section.Noise.X - 0.5f, section.Noise.Y - 0.5f) * 15.0f;
var sprite = GameMain.DecalManager.GrimeSprites[i % GameMain.DecalManager.GrimeSprites.Count];
sprite.Draw(spriteBatch, sectionPos + randomOffset, section.GetStrengthAdjustedColor(), scale: 1.25f);
}
}
}
public static void UpdateVertices(Camera cam, WaterRenderer renderer)
{
foreach (EntityGrid entityGrid in EntityGrids)
@@ -526,22 +530,38 @@ namespace Barotrauma
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
msg.WriteRangedSingle(MathHelper.Clamp(waterVolume / Volume, 0.0f, 1.5f), 0.0f, 1.5f, 8);
msg.Write(FireSources.Count > 0);
if (FireSources.Count > 0)
msg.Write(extraData != null);
if (extraData == null)
{
msg.WriteRangedInteger(Math.Min(FireSources.Count, 16), 0, 16);
for (int i = 0; i < Math.Min(FireSources.Count, 16); i++)
{
var fireSource = FireSources[i];
Vector2 normalizedPos = new Vector2(
(fireSource.Position.X - rect.X) / rect.Width,
(fireSource.Position.Y - (rect.Y - rect.Height)) / rect.Height);
msg.WriteRangedSingle(MathHelper.Clamp(waterVolume / Volume, 0.0f, 1.5f), 0.0f, 1.5f, 8);
msg.WriteRangedSingle(MathHelper.Clamp(normalizedPos.X, 0.0f, 1.0f), 0.0f, 1.0f, 8);
msg.WriteRangedSingle(MathHelper.Clamp(normalizedPos.Y, 0.0f, 1.0f), 0.0f, 1.0f, 8);
msg.WriteRangedSingle(MathHelper.Clamp(fireSource.Size.X / rect.Width, 0.0f, 1.0f), 0, 1.0f, 8);
msg.Write(FireSources.Count > 0);
if (FireSources.Count > 0)
{
msg.WriteRangedInteger(Math.Min(FireSources.Count, 16), 0, 16);
for (int i = 0; i < Math.Min(FireSources.Count, 16); i++)
{
var fireSource = FireSources[i];
Vector2 normalizedPos = new Vector2(
(fireSource.Position.X - rect.X) / rect.Width,
(fireSource.Position.Y - (rect.Y - rect.Height)) / rect.Height);
msg.WriteRangedSingle(MathHelper.Clamp(normalizedPos.X, 0.0f, 1.0f), 0.0f, 1.0f, 8);
msg.WriteRangedSingle(MathHelper.Clamp(normalizedPos.Y, 0.0f, 1.0f), 0.0f, 1.0f, 8);
msg.WriteRangedSingle(MathHelper.Clamp(fireSource.Size.X / rect.Width, 0.0f, 1.0f), 0, 1.0f, 8);
}
}
}
else
{
int sectorToUpdate = (int)extraData[0];
int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
msg.WriteRangedInteger(sectorToUpdate, 0, BackgroundSections.Count - 1);
for (int i = start; i < end; i++)
{
msg.WriteRangedSingle(BackgroundSections[i].ColorStrength, 0.0f, 1.0f, 8);
msg.Write(BackgroundSections[i].Color.PackedValue);
}
}
}
@@ -565,6 +585,54 @@ namespace Barotrauma
}
}
bool hasExtraData = message.ReadBoolean();
if (hasExtraData)
{
bool hasSectionUpdate = message.ReadBoolean();
if (hasSectionUpdate)
{
int sectorToUpdate = message.ReadRangedInteger(0, BackgroundSections.Count - 1);
int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
for (int i = start; i < end; i++)
{
float colorStrength = message.ReadRangedSingle(0.0f, 1.0f, 8);
Color color = new Color(message.ReadUInt32());
float prevColorStrength = BackgroundSections[i].ColorStrength;
BackgroundSections[i].SetColorStrength(colorStrength);
BackgroundSections[i].SetColor(color);
paintAmount = Math.Max(0, paintAmount + (BackgroundSections[i].ColorStrength - prevColorStrength) / BackgroundSections.Count);
var remoteBackgroundSection = remoteBackgroundSections.Find(s => s.Index == i);
if (remoteBackgroundSection != null)
{
remoteBackgroundSection.SetColorStrength(colorStrength);
remoteBackgroundSection.SetColor(color);
}
else
{
remoteBackgroundSections.Add(new BackgroundSection(new Rectangle(0, 0, 1, 1), i, colorStrength, color, 0));
}
}
paintAmount = BackgroundSections.Sum(s => s.ColorStrength);
}
else
{
int decalCount = message.ReadRangedInteger(0, MaxDecalsPerHull);
decals.Clear();
for (int i = 0; i < decalCount; i++)
{
UInt32 decalId = message.ReadUInt32();
float normalizedXPos = message.ReadRangedSingle(0.0f, 1.0f, 8);
float normalizedYPos = message.ReadRangedSingle(0.0f, 1.0f, 8);
float decalPosX = MathHelper.Lerp(rect.X, rect.Right, normalizedXPos);
float decalPosY = MathHelper.Lerp(rect.Y - rect.Height, rect.Y, normalizedYPos);
float decalScale = message.ReadRangedSingle(0.0f, 2.0f, 12);
AddDecal(decalId, new Vector2(decalPosX, decalPosY), decalScale, isNetworkEvent: true);
}
}
}
if (serverUpdateDelay > 0.0f) { return; }
ApplyRemoteState();
@@ -572,10 +640,14 @@ namespace Barotrauma
private void ApplyRemoteState()
{
if (remoteFireSources == null)
foreach (BackgroundSection remoteBackgroundSection in remoteBackgroundSections)
{
return;
BackgroundSections[remoteBackgroundSection.Index].SetColor(remoteBackgroundSection.Color);
BackgroundSections[remoteBackgroundSection.Index].SetColorStrength(remoteBackgroundSection.ColorStrength);
}
remoteBackgroundSections.Clear();
if (remoteFireSources == null) { return; }
WaterVolume = remoteWaterVolume;
OxygenPercentage = remoteOxygenPercentage;
@@ -609,7 +681,6 @@ namespace Barotrauma
FireSources.RemoveAt(i);
}
}
remoteFireSources = null;
}
}

View File

@@ -34,8 +34,8 @@ namespace Barotrauma
public const int DefaultBufferSize = 2000;
public const int DefaultIndoorsBufferSize = 3000;
public static Vector2 DistortionScale = new Vector2(2f, 2f);
public static Vector2 DistortionStrength = new Vector2(0.25f, 0.25f);
public static Vector2 DistortionScale = new Vector2(2f, 1.5f);
public static Vector2 DistortionStrength = new Vector2(0.01f, 0.33f);
public static float BlurAmount = 0.0f;
public Vector2 WavePos

View File

@@ -2,6 +2,7 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SharpFont;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -87,6 +88,12 @@ namespace Barotrauma.Lights
}
}
class VectorPair
{
public Vector2? A = null;
public Vector2? B = null;
}
class ConvexHull
{
public static List<ConvexHullList> HullLists = new List<ConvexHullList>();
@@ -96,6 +103,7 @@ namespace Barotrauma.Lights
private readonly Segment[] segments = new Segment[4];
private readonly SegmentPoint[] vertices = new SegmentPoint[4];
private readonly SegmentPoint[] losVertices = new SegmentPoint[4];
private readonly VectorPair[] losOffsets = new VectorPair[4];
private readonly bool[] backFacing;
private readonly bool[] ignoreEdge;
@@ -105,6 +113,7 @@ namespace Barotrauma.Lights
public VertexPositionColor[] ShadowVertices { get; private set; }
public VertexPositionTexture[] PenumbraVertices { get; private set; }
public int ShadowVertexCount { get; private set; }
public int PenumbraVertexCount { get; private set; }
private readonly HashSet<ConvexHull> overlappingHulls = new HashSet<ConvexHull>();
@@ -157,15 +166,24 @@ namespace Barotrauma.Lights
ParentEntity = parent;
ShadowVertices = new VertexPositionColor[6 * 2];
PenumbraVertices = new VertexPositionTexture[6];
ShadowVertices = new VertexPositionColor[6 * 4];
PenumbraVertices = new VertexPositionTexture[6 * 4];
backFacing = new bool[4];
ignoreEdge = new bool[4];
SetVertices(points);
Enabled = true;
float minX = points[0].X, minY = points[0].Y, maxX = points[0].X, maxY = points[0].Y;
for (int i = 1; i < vertices.Length; i++)
{
if (points[i].X < minX) minX = points[i].X;
if (points[i].Y < minY) minY = points[i].Y;
if (points[i].X > maxX) maxX = points[i].X;
if (points[i].Y > minY) maxY = points[i].Y;
}
BoundingBox = new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
isHorizontal = BoundingBox.Width > BoundingBox.Height;
if (ParentEntity is Structure structure)
@@ -180,6 +198,10 @@ namespace Barotrauma.Lights
if (door != null) { isHorizontal = door.IsHorizontal; }
}
SetVertices(points);
Enabled = true;
var chList = HullLists.Find(h => h.Submarine == parent.Submarine);
if (chList == null)
{
@@ -202,11 +224,17 @@ namespace Barotrauma.Lights
if (isHorizontal == ch.isHorizontal)
{
if (BoundingBox == ch.BoundingBox) { return; }
//hide segments that are roughly at the some position as some other segment (e.g. the ends of two adjacent wall pieces)
float mergeDist = 32;
float mergeDist = 16;
float mergeDistSqr = mergeDist * mergeDist;
Rectangle intersection = Rectangle.Intersect(BoundingBox, ch.BoundingBox);
int intersectionArea = intersection.Width * intersection.Height;
int bboxArea = BoundingBox.Width * BoundingBox.Height;
int otherBboxArea = ch.BoundingBox.Width * ch.BoundingBox.Height;
if (Math.Abs(intersectionArea - bboxArea) < mergeDistSqr) { return; }
if (Math.Abs(intersectionArea - otherBboxArea) < mergeDistSqr) { return; }
for (int i = 0; i < segments.Length; i++)
{
for (int j = 0; j < ch.segments.Length; j++)
@@ -237,21 +265,67 @@ namespace Barotrauma.Lights
}
}
}
else
for (int i = 0; i < segments.Length; i++)
{
//TODO: do something to corner areas where a vertical wall meets a horizontal one
if (ignoreEdge[i]) { continue; }
if (Vector2.DistanceSquared(segments[i].Start.Pos, segments[i].End.Pos) < 1.0f) { continue; }
for (int j = 0; j < ch.segments.Length; j++)
{
if (ch.ignoreEdge[j]) { continue; }
if (Vector2.DistanceSquared(ch.segments[j].Start.Pos, ch.segments[j].End.Pos) < 1.0f) { continue; }
if (IsSegmentAInB(segments[i], ch.segments[j]))
{
ignoreEdge[i] = true;
if (Vector2.DistanceSquared(ch.segments[j].Start.Pos, segments[i].Start.Pos) < 4.0f)
{
ch.ShiftSegmentPoint(j, false, segments[i].End.Pos);
}
else if (Vector2.DistanceSquared(ch.segments[j].Start.Pos, segments[i].End.Pos) < 4.0f)
{
ch.ShiftSegmentPoint(j, false, segments[i].Start.Pos);
}
if (Vector2.DistanceSquared(ch.segments[j].End.Pos, segments[i].Start.Pos) < 4.0f)
{
ch.ShiftSegmentPoint(j, true, segments[i].End.Pos);
}
else if (Vector2.DistanceSquared(ch.segments[j].End.Pos, segments[i].End.Pos) < 4.0f)
{
ch.ShiftSegmentPoint(j, true, segments[i].Start.Pos);
}
}
else if (IsSegmentAInB(ch.segments[j], segments[i]))
{
ch.ignoreEdge[j] = true;
if (Vector2.DistanceSquared(segments[i].Start.Pos, ch.segments[j].Start.Pos) < 4.0f)
{
ShiftSegmentPoint(i, false, ch.segments[j].End.Pos);
}
else if (Vector2.DistanceSquared(segments[i].Start.Pos, ch.segments[j].End.Pos) < 4.0f)
{
ShiftSegmentPoint(i, false, ch.segments[j].Start.Pos);
}
if (Vector2.DistanceSquared(segments[i].End.Pos, ch.segments[j].Start.Pos) < 4.0f)
{
ShiftSegmentPoint(i, true, ch.segments[j].End.Pos);
}
else if (Vector2.DistanceSquared(segments[i].End.Pos, ch.segments[j].End.Pos) < 4.0f)
{
ShiftSegmentPoint(i, true, ch.segments[j].Start.Pos);
}
}
}
}
//ignore edges that are inside some other convex hull
for (int i = 0; i < vertices.Length; i++)
{
if (vertices[i].Pos.X >= ch.BoundingBox.X && vertices[i].Pos.X <= ch.BoundingBox.Right &&
vertices[i].Pos.Y >= ch.BoundingBox.Y && vertices[i].Pos.Y <= ch.BoundingBox.Bottom)
if (ch.IsPointInside(vertices[i].Pos))
{
Vector2 p = vertices[(i + 1) % vertices.Length].Pos;
if (p.X >= ch.BoundingBox.X && p.X <= ch.BoundingBox.Right &&
p.Y >= ch.BoundingBox.Y && p.Y <= ch.BoundingBox.Bottom)
if (ch.IsPointInside(vertices[(i + 1) % vertices.Length].Pos))
{
ignoreEdge[i] = true;
overlappingHulls.Add(ch);
@@ -260,6 +334,75 @@ namespace Barotrauma.Lights
}
}
private void ShiftSegmentPoint(int segmentIndex, bool end, Vector2 newPos)
{
var segment = segments[segmentIndex];
losOffsets[segmentIndex] ??= new VectorPair();
bool flipped = false;
if (Vector2.DistanceSquared(vertices[segmentIndex].Pos, segment.Start.Pos) > Vector2.DistanceSquared(vertices[segmentIndex].Pos, segment.End.Pos))
{
flipped = true;
}
if (end == !flipped)
{
losOffsets[segmentIndex].B = newPos;
}
else
{
losOffsets[segmentIndex].A = newPos;
}
}
public bool IsSegmentAInB(Segment a, Segment b)
{
if (Vector2.DistanceSquared(a.Start.Pos, a.End.Pos) > Vector2.DistanceSquared(b.Start.Pos, b.End.Pos))
{
return false;
}
Vector2 min = new Vector2(Math.Min(b.Start.Pos.X, b.End.Pos.X), Math.Min(b.Start.Pos.Y, b.End.Pos.Y));
Vector2 max = new Vector2(Math.Max(b.Start.Pos.X, b.End.Pos.X), Math.Max(b.Start.Pos.Y, b.End.Pos.Y));
min.X -= 1.0f; min.Y -= 1.0f;
max.X += 1.0f; max.Y += 1.0f;
if (a.Start.Pos.X < min.X) { return false; }
if (a.Start.Pos.Y < min.Y) { return false; }
if (a.End.Pos.X < min.X) { return false; }
if (a.End.Pos.Y < min.Y) { return false; }
if (a.Start.Pos.X > max.X) { return false; }
if (a.Start.Pos.Y > max.Y) { return false; }
if (a.End.Pos.X > max.X) { return false; }
if (a.End.Pos.Y > max.Y) { return false; }
float startDist = MathUtils.LineToPointDistanceSquared(b.Start.Pos, b.End.Pos, a.Start.Pos);
if (startDist > 1.0f) { return false; }
float endDist = MathUtils.LineToPointDistanceSquared(b.Start.Pos, b.End.Pos, a.End.Pos);
if (endDist > 1.0f) { return false; }
return true;
}
public bool IsPointInside(Vector2 point)
{
if (!BoundingBox.Contains(point)) { return false; }
Vector2 center = (vertices[0].Pos + vertices[1].Pos + vertices[2].Pos + vertices[3].Pos) * 0.25f;
for (int i=0;i<4;i++)
{
Vector2 segmentVector = vertices[(i + 1) % 4].Pos - vertices[i].Pos;
Vector2 centerToVertex = center - vertices[i].Pos;
Vector2 pointToVertex = point - vertices[i].Pos;
float dotCenter = Vector2.Dot(centerToVertex, segmentVector);
float dotPoint = Vector2.Dot(pointToVertex, segmentVector);
if ((dotCenter > 0f && dotPoint < 0f) || (dotCenter < 0f && dotPoint > 0f)) { return false; }
}
return true;
}
private void MergeSegments(Segment segment1, Segment segment2, bool startPointsMatch)
{
int startPointIndex = -1, endPointIndex = -1;
@@ -344,6 +487,8 @@ namespace Barotrauma.Lights
vertices[i].Pos += amount;
losVertices[i].Pos += amount;
losOffsets[i] = null;
segments[i].Start.Pos += amount;
segments[i].End.Pos += amount;
}
@@ -406,6 +551,7 @@ namespace Barotrauma.Lights
{
vertices[i] = new SegmentPoint(points[i], this);
losVertices[i] = new SegmentPoint(points[i], this);
losOffsets[i] = null;
}
for (int i = 0; i < 4; i++)
@@ -517,7 +663,7 @@ namespace Barotrauma.Lights
}
}
public void CalculateShadowVertices(Vector2 lightSourcePos, bool los = true)
public void CalculateLosVertices(Vector2 lightSourcePos)
{
Vector3 offset = Vector3.Zero;
if (ParentEntity != null && ParentEntity.Submarine != null)
@@ -527,8 +673,6 @@ namespace Barotrauma.Lights
ShadowVertexCount = 0;
var vertices = los ? losVertices : this.vertices;
//compute facing of each edge, using N*L
for (int i = 0; i < 4; i++)
{
@@ -538,8 +682,8 @@ namespace Barotrauma.Lights
continue;
}
Vector2 firstVertex = vertices[i].Pos;
Vector2 secondVertex = vertices[(i+1) % 4].Pos;
Vector2 firstVertex = losVertices[i].Pos;
Vector2 secondVertex = losVertices[(i+1) % 4].Pos;
Vector2 L = lightSourcePos - ((firstVertex + secondVertex) / 2.0f);
@@ -547,121 +691,144 @@ namespace Barotrauma.Lights
-(secondVertex.Y - firstVertex.Y),
secondVertex.X - firstVertex.X);
backFacing[i] = (Vector2.Dot(N, L) < 0) == los;
backFacing[i] = (Vector2.Dot(N, L) < 0);
}
//find beginning and ending vertices which
//belong to the shadow
int startingIndex = -1;
int endingIndex = -1;
for (int i = 0; i < 4; i++)
ShadowVertexCount = 0;
for (int i=0;i<4;i++)
{
int currentEdge = i;
int nextEdge = (i + 1) % 4;
if (!backFacing[i]) { continue; }
int currentIndex = i;
Vector3 vertexPos0 = new Vector3(losOffsets[currentIndex]?.A ?? losVertices[currentIndex].Pos, 0.0f);
Vector3 vertexPos1 = new Vector3(losOffsets[currentIndex]?.B ?? losVertices[(currentIndex + 1) % 4].Pos, 0.0f);
if (backFacing[currentEdge] && !backFacing[nextEdge])
endingIndex = nextEdge;
if (Vector3.DistanceSquared(vertexPos0, vertexPos1) < 1.0f) { continue; }
if (!backFacing[currentEdge] && backFacing[nextEdge])
startingIndex = nextEdge;
}
Vector3 L2P0 = vertexPos0 - new Vector3(lightSourcePos, 0);
L2P0.Normalize();
Vector3 extruded0 = new Vector3(lightSourcePos, 0) + L2P0 * 9000;
if (startingIndex == -1 || endingIndex == -1) { return; }
Vector3 L2P1 = vertexPos1 - new Vector3(lightSourcePos, 0);
L2P1.Normalize();
Vector3 extruded1 = new Vector3(lightSourcePos, 0) + L2P1 * 9000;
//nr of vertices that are in the shadow
if (endingIndex > startingIndex)
ShadowVertexCount = endingIndex - startingIndex + 1;
else
ShadowVertexCount = 4 + 1 - startingIndex + endingIndex;
//shadowVertices = new VertexPositionColor[shadowVertexCount * 2];
//create a triangle strip that has the shape of the shadow
int currentIndex = startingIndex;
int svCount = 0;
while (svCount != ShadowVertexCount * 2)
{
Vector3 vertexPos = new Vector3(vertices[currentIndex].Pos, 0.0f);
int i = los ? svCount : svCount + 1;
int j = los ? svCount + 1 : svCount;
//one vertex on the hull
ShadowVertices[i] = new VertexPositionColor
ShadowVertices[ShadowVertexCount + 0] = new VertexPositionColor
{
Color = los ? Color.Black : Color.Transparent,
Position = vertexPos + offset
Color = Color.Black,
Position = vertexPos1 + offset
};
//one extruded by the light direction
ShadowVertices[j] = new VertexPositionColor
ShadowVertices[ShadowVertexCount + 1] = new VertexPositionColor
{
Color = ShadowVertices[i].Color
Color = Color.Black,
Position = vertexPos0 + offset
};
Vector3 L2P = vertexPos - new Vector3(lightSourcePos, 0);
L2P.Normalize();
ShadowVertices[j].Position = new Vector3(lightSourcePos, 0) + L2P * 9000 + offset;
ShadowVertices[ShadowVertexCount + 2] = new VertexPositionColor
{
Color = Color.Black,
Position = extruded0 + offset
};
svCount += 2;
currentIndex = (currentIndex + 1) % 4;
ShadowVertices[ShadowVertexCount + 3] = new VertexPositionColor
{
Color = Color.Black,
Position = vertexPos1 + offset
};
ShadowVertices[ShadowVertexCount + 4] = new VertexPositionColor
{
Color = Color.Black,
Position = extruded0 + offset
};
ShadowVertices[ShadowVertexCount + 5] = new VertexPositionColor
{
Color = Color.Black,
Position = extruded1 + offset
};
ShadowVertexCount += 6;
}
if (los)
{
CalculatePenumbraVertices(startingIndex, endingIndex, lightSourcePos, los);
}
CalculateLosPenumbraVertices(lightSourcePos);
}
private void CalculatePenumbraVertices(int startingIndex, int endingIndex, Vector2 lightSourcePos, bool los)
private void CalculateLosPenumbraVertices(Vector2 lightSourcePos)
{
var vertices = los ? losVertices : this.vertices;
Vector3 offset = Vector3.Zero;
if (ParentEntity != null && ParentEntity.Submarine != null)
{
offset = new Vector3(ParentEntity.Submarine.DrawPosition.X, ParentEntity.Submarine.DrawPosition.Y, 0.0f);
}
for (int n = 0; n < 4; n += 3)
PenumbraVertexCount = 0;
for (int i = 0; i < 4; i++)
{
Vector3 penumbraStart = new Vector3((n == 0) ? vertices[startingIndex].Pos : vertices[endingIndex].Pos, 0.0f);
int currentIndex = i;
int prevIndex = (i + 3) % 4;
int nextIndex = (i + 1) % 4;
bool disjointed = losOffsets[i]?.A != null;
Vector2 vertexPos0 = losOffsets[currentIndex]?.A ?? losVertices[currentIndex].Pos;
Vector2 vertexPos1 = losOffsets[currentIndex]?.B ?? losVertices[nextIndex].Pos;
PenumbraVertices[n] = new VertexPositionTexture
if (Vector2.DistanceSquared(vertexPos0, vertexPos1) < 1.0f) { continue; }
if (backFacing[currentIndex] && (disjointed || (!backFacing[prevIndex])))
{
Position = penumbraStart + offset,
TextureCoordinate = new Vector2(0.0f, 1.0f)
};
Vector3 penumbraStart = new Vector3(vertexPos0, 0.0f);
for (int i = 0; i < 2; i++)
{
PenumbraVertices[n + i + 1] = new VertexPositionTexture();
Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
vertexDir.Normalize();
Vector3 normal = (i == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
if (n > 0) normal = -normal;
vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) - normal * 20.0f);
vertexDir.Normalize();
PenumbraVertices[n + i + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000 + offset;
if (los)
PenumbraVertices[PenumbraVertexCount] = new VertexPositionTexture
{
PenumbraVertices[n + i + 1].TextureCoordinate = (i == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
}
else
Position = penumbraStart + offset,
TextureCoordinate = new Vector2(0.0f, 1.0f)
};
for (int j = 0; j < 2; j++)
{
PenumbraVertices[n + i + 1].TextureCoordinate = (i == 0) ? new Vector2(1.0f, 0.0f) : Vector2.Zero;
PenumbraVertices[PenumbraVertexCount + j + 1] = new VertexPositionTexture();
Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
vertexDir.Normalize();
Vector3 normal = (j == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) - normal * 20.0f);
vertexDir.Normalize();
PenumbraVertices[PenumbraVertexCount + j + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000 + offset;
PenumbraVertices[PenumbraVertexCount + j + 1].TextureCoordinate = (j == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
}
PenumbraVertexCount += 3;
}
if (n > 0)
disjointed = losOffsets[i]?.B != null;
if (backFacing[currentIndex] && (disjointed || (!backFacing[nextIndex])))
{
var temp = PenumbraVertices[4];
PenumbraVertices[4] = PenumbraVertices[5];
PenumbraVertices[5] = temp;
Vector3 penumbraStart = new Vector3(vertexPos1, 0.0f);
PenumbraVertices[PenumbraVertexCount] = new VertexPositionTexture
{
Position = penumbraStart + offset,
TextureCoordinate = new Vector2(0.0f, 1.0f)
};
for (int j = 0; j < 2; j++)
{
PenumbraVertices[PenumbraVertexCount + (1 - j) + 1] = new VertexPositionTexture();
Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
vertexDir.Normalize();
Vector3 normal = (j == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) + normal * 20.0f);
vertexDir.Normalize();
PenumbraVertices[PenumbraVertexCount + (1 - j) + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000 + offset;
PenumbraVertices[PenumbraVertexCount + (1 - j) + 1].TextureCoordinate = (j == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
}
PenumbraVertexCount += 3;
}
}
}

View File

@@ -381,7 +381,7 @@ namespace Barotrauma.Lights
if (GUI.DisableItemHighlights) { return false; }
highlightedEntities.Clear();
if (Character.Controlled != null)
if (Character.Controlled != null && (!Character.Controlled.IsKeyDown(InputType.Aim) || Character.Controlled.SelectedItems.Any(it => it?.GetComponent<Sprayer>() == null)))
{
if (Character.Controlled.FocusedItem != null)
{
@@ -532,28 +532,16 @@ namespace Barotrauma.Lights
Vector2 relativeLightPos = pos;
if (convexHull.ParentEntity?.Submarine != null) relativeLightPos -= convexHull.ParentEntity.Submarine.Position;
convexHull.CalculateShadowVertices(relativeLightPos, true);
convexHull.CalculateLosVertices(relativeLightPos);
//convert triangle strips to a triangle list
for (int i = 0; i < convexHull.ShadowVertexCount * 2 - 2; i++)
for (int i = 0; i < convexHull.ShadowVertexCount; i++)
{
if (i % 2 == 0)
{
shadowVerts.Add(convexHull.ShadowVertices[i]);
shadowVerts.Add(convexHull.ShadowVertices[i + 1]);
shadowVerts.Add(convexHull.ShadowVertices[i + 2]);
}
else
{
shadowVerts.Add(convexHull.ShadowVertices[i]);
shadowVerts.Add(convexHull.ShadowVertices[i + 2]);
shadowVerts.Add(convexHull.ShadowVertices[i + 1]);
}
shadowVerts.Add(convexHull.ShadowVertices[i]);
}
if (convexHull.ShadowVertexCount > 0)
for (int i = 0; i < convexHull.PenumbraVertexCount; i++)
{
penumbraVerts.AddRange(convexHull.PenumbraVertices);
penumbraVerts.Add(convexHull.PenumbraVertices[i]);
}
}

View File

@@ -140,7 +140,7 @@ namespace Barotrauma.Lights
private short[] indices;
private List<ConvexHullList> hullsInRange;
public Texture2D texture;
public SpriteEffects LightSpriteEffect;
@@ -300,6 +300,14 @@ namespace Barotrauma.Lights
}
}
public float TextureRange
{
get
{
return lightSourceParams.TextureRange;
}
}
/// <summary>
/// Background lights are drawn behind submarines and they don't cast shadows.
/// </summary>
@@ -366,7 +374,7 @@ namespace Barotrauma.Lights
var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub);
if (fullChList == null) return;
chList.List = fullChList.List.FindAll(ch => ch.Enabled && MathUtils.CircleIntersectsRectangle(lightPos, Range, ch.BoundingBox));
chList.List = fullChList.List.FindAll(ch => ch.Enabled && MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, ch.BoundingBox));
NeedsHullCheck = true;
}
@@ -418,7 +426,7 @@ namespace Barotrauma.Lights
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//only draw if the light overlaps with the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, Range, subBorders))
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, subBorders))
{
if (chList.List.Count > 0) NeedsRecalculation = true;
chList.List.Clear();
@@ -452,7 +460,7 @@ namespace Barotrauma.Lights
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//don't draw any shadows if the light doesn't overlap with the borders of the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, Range, subBorders))
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, subBorders))
{
if (chList.List.Count > 0) NeedsRecalculation = true;
chList.List.Clear();
@@ -495,7 +503,7 @@ namespace Barotrauma.Lights
{
foreach (ConvexHull hull in chList.List)
{
if (!chList.IsHidden.Contains(hull)) hulls.Add(hull);
if (!chList.IsHidden.Contains(hull)) { hulls.Add(hull); }
}
foreach (ConvexHull hull in chList.List)
{
@@ -503,7 +511,7 @@ namespace Barotrauma.Lights
}
}
float bounds = Range * 2;
float bounds = TextureRange;
//find convexhull segments that are close enough and facing towards the light source
List<Segment> visibleSegments = new List<Segment>();
List<SegmentPoint> points = new List<SegmentPoint>();
@@ -513,6 +521,49 @@ namespace Barotrauma.Lights
hull.GetVisibleSegments(drawPos, visibleSegments, ignoreEdges: false);
}
//add a square-shaped boundary to make sure we've got something to construct the triangles from
//even if there aren't enough hull segments around the light source
//(might be more effective to calculate if we actually need these extra points)
Vector2 drawOffset = Vector2.Zero;
float boundsExtended = bounds;
if (OverrideLightTexture != null)
{
float cosAngle = (float)Math.Cos(Rotation);
float sinAngle = -(float)Math.Sin(Rotation);
var overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
Vector2 origin = OverrideLightTexture.Origin;
origin /= Math.Max(overrideTextureDims.X, overrideTextureDims.Y);
origin -= Vector2.One * 0.5f;
if (Math.Abs(origin.X) >= 0.45f || Math.Abs(origin.Y) >= 0.45f)
{
boundsExtended += 5.0f;
}
origin *= TextureRange;
drawOffset.X = -origin.X * cosAngle - origin.Y * sinAngle;
drawOffset.Y = origin.X * sinAngle + origin.Y * cosAngle;
}
var boundaryCorners = new SegmentPoint[] {
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X + boundsExtended, drawPos.Y + drawOffset.Y + boundsExtended), null),
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X + boundsExtended, drawPos.Y + drawOffset.Y - boundsExtended), null),
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X - boundsExtended, drawPos.Y + drawOffset.Y - boundsExtended), null),
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X - boundsExtended, drawPos.Y + drawOffset.Y + boundsExtended), null)
};
for (int i = 0; i < 4; i++)
{
var s = new Segment(boundaryCorners[i], boundaryCorners[(i + 1) % 4], null);
visibleSegments.Add(s);
}
//Generate new points at the intersections between segments
//This is necessary for the light volume to generate properly on some subs
for (int i = 0; i < visibleSegments.Count; i++)
@@ -532,10 +583,10 @@ namespace Barotrauma.Lights
Vector2 p2a = visibleSegments[j].Start.WorldPos;
Vector2 p2b = visibleSegments[j].End.WorldPos;
if (Vector2.DistanceSquared(p1a, p2a) < 25.0f ||
Vector2.DistanceSquared(p1a, p2b) < 25.0f ||
Vector2.DistanceSquared(p1b, p2a) < 25.0f ||
Vector2.DistanceSquared(p1b, p2b) < 25.0f)
if (Vector2.DistanceSquared(p1a, p2a) < 5.0f ||
Vector2.DistanceSquared(p1a, p2b) < 5.0f ||
Vector2.DistanceSquared(p1b, p2a) < 5.0f ||
Vector2.DistanceSquared(p1b, p2b) < 5.0f)
{
continue;
}
@@ -565,8 +616,8 @@ namespace Barotrauma.Lights
mid.Pos -= visibleSegments[i].ConvexHull.ParentEntity.Submarine.DrawPosition;
}
if (Vector2.DistanceSquared(start.WorldPos, mid.WorldPos) < 25.0f ||
Vector2.DistanceSquared(end.WorldPos, mid.WorldPos) < 25.0f)
if (Vector2.DistanceSquared(start.WorldPos, mid.WorldPos) < 5.0f ||
Vector2.DistanceSquared(end.WorldPos, mid.WorldPos) < 5.0f)
{
continue;
}
@@ -580,6 +631,7 @@ namespace Barotrauma.Lights
{
IsHorizontal = visibleSegments[i].IsHorizontal
};
visibleSegments[i] = seg1;
visibleSegments.Insert(i + 1, seg2);
i--;
@@ -588,34 +640,27 @@ namespace Barotrauma.Lights
}
}
foreach (Segment s in visibleSegments)
//remove segments that fall out of bounds
for (int i = 0; i < visibleSegments.Count; i++)
{
points.Add(s.Start);
points.Add(s.End);
if (Math.Abs(s.Start.WorldPos.X - drawPos.X) > bounds) bounds = Math.Abs(s.Start.WorldPos.X - drawPos.X);
if (Math.Abs(s.Start.WorldPos.Y - drawPos.Y) > bounds) bounds = Math.Abs(s.Start.WorldPos.Y - drawPos.Y);
if (Math.Abs(s.End.WorldPos.X - drawPos.X) > bounds) bounds = Math.Abs(s.End.WorldPos.X - drawPos.X);
if (Math.Abs(s.End.WorldPos.Y - drawPos.Y) > bounds) bounds = Math.Abs(s.End.WorldPos.Y - drawPos.Y);
Segment s = visibleSegments[i];
if (Math.Abs(s.Start.WorldPos.X - drawPos.X - drawOffset.X) > boundsExtended + 1.0f ||
Math.Abs(s.Start.WorldPos.Y - drawPos.Y - drawOffset.Y) > boundsExtended + 1.0f ||
Math.Abs(s.End.WorldPos.X - drawPos.X - drawOffset.X) > boundsExtended + 1.0f ||
Math.Abs(s.End.WorldPos.Y - drawPos.Y - drawOffset.Y) > boundsExtended + 1.0f)
{
visibleSegments.RemoveAt(i);
i--;
}
else
{
points.Add(s.Start);
points.Add(s.End);
}
}
//add a square-shaped boundary to make sure we've got something to construct the triangles from
//even if there aren't enough hull segments around the light source
visibleSegments = visibleSegments.OrderBy(s => MathUtils.LineToPointDistanceSquared(s.Start.WorldPos, s.End.WorldPos, drawPos)).ToList();
//(might be more effective to calculate if we actually need these extra points)
var boundaryCorners = new List<SegmentPoint> {
new SegmentPoint(new Vector2(drawPos.X + bounds, drawPos.Y + bounds), null),
new SegmentPoint(new Vector2(drawPos.X + bounds, drawPos.Y - bounds), null),
new SegmentPoint(new Vector2(drawPos.X - bounds, drawPos.Y - bounds), null),
new SegmentPoint(new Vector2(drawPos.X - bounds, drawPos.Y + bounds), null)
};
points.AddRange(boundaryCorners);
for (int i = 0; i < 4; i++)
{
visibleSegments.Add(new Segment(boundaryCorners[i], boundaryCorners[(i + 1) % 4], null));
}
var compareCCW = new CompareSegmentPointCW(drawPos);
try
{
@@ -635,13 +680,15 @@ namespace Barotrauma.Lights
//List<Pair<int, Vector2>> preOutput = new List<Pair<int, Vector2>>();
//remove points that are very close to each other
for (int i = 0; i < points.Count - 1; i++)
for (int i = 0; i < points.Count; i++)
{
if (Math.Abs(points[i].WorldPos.X - points[i + 1].WorldPos.X) < 6 &&
Math.Abs(points[i].WorldPos.Y - points[i + 1].WorldPos.Y) < 6)
for (int j = Math.Min(i + 4, points.Count-1); j > i; j--)
{
points.RemoveAt(i + 1);
i--;
if (Math.Abs(points[i].WorldPos.X - points[j].WorldPos.X) < 6 &&
Math.Abs(points[i].WorldPos.Y - points[j].WorldPos.Y) < 6)
{
points.RemoveAt(j);
}
}
}
@@ -651,16 +698,16 @@ namespace Barotrauma.Lights
Vector2 dirNormal = new Vector2(-dir.Y, dir.X) * 3;
//do two slightly offset raycasts to hit the segment itself and whatever's behind it
Pair<int,Vector2> intersection1 = RayCast(drawPos, drawPos + dir * bounds * 2 - dirNormal, visibleSegments);
Pair<int,Vector2> intersection2 = RayCast(drawPos, drawPos + dir * bounds * 2 + dirNormal, visibleSegments);
Pair<int,Vector2> intersection1 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 - dirNormal, visibleSegments);
Pair<int,Vector2> intersection2 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 + dirNormal, visibleSegments);
if (intersection1.First < 0) return new List<Vector2>();
if (intersection2.First < 0) return new List<Vector2>();
if (intersection1.First < 0) return null;
if (intersection2.First < 0) return null;
Segment seg1 = visibleSegments[intersection1.First];
Segment seg2 = visibleSegments[intersection2.First];
bool isPoint1 = MathUtils.LineToPointDistance(seg1.Start.WorldPos, seg1.End.WorldPos, p.WorldPos) < 5.0f;
bool isPoint2 = MathUtils.LineToPointDistance(seg2.Start.WorldPos, seg2.End.WorldPos, p.WorldPos) < 5.0f;
bool isPoint1 = MathUtils.LineToPointDistanceSquared(seg1.Start.WorldPos, seg1.End.WorldPos, p.WorldPos) < 25.0f;
bool isPoint2 = MathUtils.LineToPointDistanceSquared(seg2.Start.WorldPos, seg2.End.WorldPos, p.WorldPos) < 25.0f;
if (isPoint1 && isPoint2)
{
@@ -696,11 +743,13 @@ namespace Barotrauma.Lights
//remove points that are very close to each other
for (int i = 0; i < output.Count - 1; i++)
{
if (Math.Abs(output[i].X - output[i + 1].X) < 6 &&
Math.Abs(output[i].Y - output[i + 1].Y) < 6)
for (int j = Math.Min(i + 4, output.Count - 1); j > i; j--)
{
output.RemoveAt(i + 1);
i--;
if (Math.Abs(output[i].X - output[j].X) < 6 &&
Math.Abs(output[i].Y - output[j].Y) < 6)
{
output.RemoveAt(j);
}
}
}
@@ -709,7 +758,6 @@ namespace Barotrauma.Lights
private Pair<int, Vector2> RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments)
{
float closestDist = float.PositiveInfinity;
Vector2? closestIntersection = null;
int segment = -1;
@@ -724,11 +772,11 @@ namespace Barotrauma.Lights
//segment's end position always has a higher or equal y coordinate than the start position
//so we can do this comparison and skip segments that are at the wrong side of the ray
if (s.End.WorldPos.Y < s.Start.WorldPos.Y)
/*if (s.End.WorldPos.Y < s.Start.WorldPos.Y)
{
System.Diagnostics.Debug.Assert(s.End.WorldPos.Y >= s.Start.WorldPos.Y,
"LightSource raycast failed. Segment's end positions should never be below the start position. Parent entity: " + (s.ConvexHull?.ParentEntity == null ? "null" : s.ConvexHull.ParentEntity.ToString()));
}
}*/
if (s.Start.WorldPos.Y > maxY || s.End.WorldPos.Y < minY) { continue; }
//same for the x-axis
if (s.Start.WorldPos.X > s.End.WorldPos.X)
@@ -741,18 +789,29 @@ namespace Barotrauma.Lights
if (s.End.WorldPos.X < minX) continue;
if (s.Start.WorldPos.X > maxX) continue;
}
if (s.IsAxisAligned ?
MathUtils.GetAxisAlignedLineIntersection(rayStart, rayEnd, s.Start.WorldPos, s.End.WorldPos, s.IsHorizontal, out Vector2 intersection) :
MathUtils.GetLineIntersection(rayStart, rayEnd, s.Start.WorldPos, s.End.WorldPos, out intersection))
bool intersects;
Vector2 intersection;
if (s.IsAxisAligned)
{
float dist = Vector2.DistanceSquared(intersection, rayStart);
if (dist < closestDist)
{
closestDist = dist;
closestIntersection = intersection;
segment = i;
}
intersects = MathUtils.GetAxisAlignedLineIntersection(rayStart, rayEnd, s.Start.WorldPos, s.End.WorldPos, s.IsHorizontal, out intersection);
}
else
{
intersects = MathUtils.GetLineIntersection(rayStart, rayEnd, s.Start.WorldPos, s.End.WorldPos, out intersection);
}
if (intersects)
{
closestIntersection = intersection;
rayEnd = intersection;
minX = Math.Min(rayStart.X, rayEnd.X);
maxX = Math.Max(rayStart.X, rayEnd.X);
minY = Math.Min(rayStart.Y, rayEnd.Y);
maxY = Math.Max(rayStart.Y, rayEnd.Y);
segment = i;
}
}
@@ -797,7 +856,7 @@ namespace Barotrauma.Lights
//hacky fix to exc excessively large light volumes (they used to be up to 4x the range of the light if there was nothing to block the rays).
//might want to tweak the raycast logic in a way that this isn't necessary
float boundRadius = Range * 1.1f / (1.0f - Math.Max(Math.Abs(uvOffset.X), Math.Abs(uvOffset.Y)));
/*float boundRadius = Range * 1.1f / (1.0f - Math.Max(Math.Abs(uvOffset.X), Math.Abs(uvOffset.Y)));
Rectangle boundArea = new Rectangle((int)(drawPos.X - boundRadius), (int)(drawPos.Y + boundRadius), (int)(boundRadius * 2), (int)(boundRadius * 2));
for (int i = 0; i < rayCastHits.Count; i++)
{
@@ -805,7 +864,7 @@ namespace Barotrauma.Lights
{
rayCastHits[i] = intersection;
}
}
}*/
// Add all the other encounter points as vertices
// storing their world position as UV coordinates
@@ -818,7 +877,7 @@ namespace Barotrauma.Lights
//so we can add new vertices based on these normals
Vector2 prevVertex = rayCastHits[i > 0 ? i - 1 : rayCastHits.Count - 1];
Vector2 nextVertex = rayCastHits[i < rayCastHits.Count - 1 ? i + 1 : 0];
Vector2 rawDiff = vertex - drawPos;
//calculate normal of first segment
@@ -836,12 +895,18 @@ namespace Barotrauma.Lights
//if the normal is pointing towards the light origin
//rather than away from it, invert it
if (Vector2.DistanceSquared(nDiff2, rawDiff) > Vector2.DistanceSquared(-nDiff2, rawDiff)) nDiff2 = -nDiff2;
//add the normals together and use some magic numbers to create
//a somewhat useful/good-looking blur
Vector2 nDiff = nDiff1 + nDiff2;
nDiff /= Math.Max(Math.Abs(nDiff.X), Math.Abs(nDiff.Y));
nDiff *= 50.0f;
Vector2 nDiff = nDiff1 * 40.0f;
if (MathUtils.GetLineIntersection(vertex + (nDiff1 * 40.0f), nextVertex + (nDiff1 * 40.0f), vertex + (nDiff2 * 40.0f), prevVertex + (nDiff2 * 40.0f), true, out Vector2 intersection))
{
nDiff = intersection - vertex;
if (nDiff.LengthSquared() > 10000.0f)
{
nDiff /= Math.Max(Math.Abs(nDiff.X), Math.Abs(nDiff.Y)); nDiff *= 100.0f;
}
}
Vector2 diff = rawDiff;
diff /= Range * 2.0f;
@@ -948,6 +1013,50 @@ namespace Barotrauma.Lights
/// <param name="spriteBatch"></param>
public void DrawSprite(SpriteBatch spriteBatch, Camera cam)
{
if (GameMain.DebugDraw)
{
Vector2 drawPos = position;
if (ParentSub != null)
{
drawPos += ParentSub.DrawPosition;
}
drawPos.Y = -drawPos.Y;
float cosAngle = (float)Math.Cos(Rotation);
float sinAngle = -(float)Math.Sin(Rotation);
float bounds = TextureRange;
if (OverrideLightTexture != null)
{
var overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
Vector2 origin = OverrideLightTexture.Origin;
origin /= Math.Max(overrideTextureDims.X, overrideTextureDims.Y);
origin *= TextureRange;
drawPos.X += origin.X * sinAngle + origin.Y * cosAngle;
drawPos.Y += origin.X * cosAngle + origin.Y * sinAngle;
}
//add a square-shaped boundary to make sure we've got something to construct the triangles from
//even if there aren't enough hull segments around the light source
//(might be more effective to calculate if we actually need these extra points)
var boundaryCorners = new SegmentPoint[] {
new SegmentPoint(new Vector2(drawPos.X + bounds, drawPos.Y + bounds), null),
new SegmentPoint(new Vector2(drawPos.X + bounds, drawPos.Y - bounds), null),
new SegmentPoint(new Vector2(drawPos.X - bounds, drawPos.Y - bounds), null),
new SegmentPoint(new Vector2(drawPos.X - bounds, drawPos.Y + bounds), null)
};
for (int i=0;i<4;i++)
{
GUI.DrawLine(spriteBatch, boundaryCorners[i].Pos, boundaryCorners[(i + 1) % 4].Pos, Color.White, 0, 3);
}
}
if (DeformableLightSprite != null)
{
Vector2 origin = DeformableLightSprite.Origin;
@@ -976,11 +1085,11 @@ namespace Barotrauma.Lights
if (LightSprite != null)
{
Vector2 origin = LightSprite.Origin;
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
if ((LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally)
{
origin.X = LightSprite.SourceRect.Width - origin.X;
}
if (LightSpriteEffect == SpriteEffects.FlipVertically)
if ((LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically)
{
origin.Y = LightSprite.SourceRect.Height - origin.Y;
}
@@ -1088,7 +1197,7 @@ namespace Barotrauma.Lights
PrimitiveType.TriangleList, 0, 0, indexCount / 3
);
}
public void Reset()
{
hullsInRange.Clear();

View File

@@ -9,8 +9,6 @@ namespace Barotrauma
{
partial class Map
{
public bool AllowDebugTeleport;
class MapAnim
{
public Location StartLocation;
@@ -380,6 +378,7 @@ namespace Barotrauma
Location prevLocation = CurrentDisplayLocation;
CurrentLocation = HighlightedLocation;
Level.Loaded.DebugSetStartLocation(CurrentLocation);
Level.Loaded.DebugSetEndLocation(null);
CurrentLocation.Discovered = true;
CurrentLocation.CreateStore();
@@ -573,7 +572,7 @@ namespace Barotrauma
}
}
if (GameMain.DebugDraw && location == HighlightedLocation)
if (GameMain.DebugDraw && location == HighlightedLocation && (!location.Discovered || !location.Type.HasOutpost))
{
if (location.Reputation != null)
{
@@ -589,7 +588,7 @@ namespace Barotrauma
Color barColor = ToolBox.GradientLerp(location.Reputation.NormalizedValue, Color.Red, Color.Yellow, Color.LightGreen);
GUI.DrawRectangle(spriteBatch, bgRect, Color.Black * 0.8f, isFilled: true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)dPos.X, (int)dPos.Y, (int)(location.Reputation.NormalizedValue * 255), 32), barColor, isFilled: true);
string reputationValue = location.Reputation.Value.ToString(CultureInfo.InvariantCulture);
string reputationValue = ((int)location.Reputation.Value).ToString();
Vector2 repValueSize = GUI.SubHeadingFont.MeasureString(reputationValue);
GUI.DrawString(spriteBatch, dPos + (new Vector2(256, 32) / 2) - (repValueSize / 2), reputationValue, Color.White, Color.Black, font: GUI.SubHeadingFont);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)dPos.X, (int)dPos.Y, 256, 32), Color.White);
@@ -607,12 +606,34 @@ namespace Barotrauma
Vector2 nameSize = GUI.LargeFont.MeasureString(HighlightedLocation.Name);
Vector2 typeSize = GUI.Font.MeasureString(HighlightedLocation.Type.Name);
Vector2 size = new Vector2(Math.Max(nameSize.X, typeSize.X), nameSize.Y + typeSize.Y);
bool showReputation = HighlightedLocation.Discovered && HighlightedLocation.Type.HasOutpost && HighlightedLocation.Reputation != null;
string repLabelText = null, repValueText = null;
Vector2 repLabelSize = Vector2.Zero, repBarSize = Vector2.Zero;
if (showReputation)
{
repLabelText = TextManager.Get("reputation");
repLabelSize = GUI.Font.MeasureString(repLabelText);
size.X = Math.Max(size.X, repLabelSize.X);
repBarSize = new Vector2(Math.Max(0.75f * size.X, 100), repLabelSize.Y);
size.X = Math.Max(size.X, (4.0f / 3.0f) * repBarSize.X);
size.Y += 2 * repLabelSize.Y + 4 + repBarSize.Y;
repValueText = ((int)HighlightedLocation.Reputation.Value).ToString();
}
GUI.Style.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0].Draw(
spriteBatch, new Rectangle((int)(pos.X - 60 * GUI.Scale), (int)(pos.Y - size.Y), (int)(size.X + 120 * GUI.Scale), (int)(size.Y * 2.2f)), Color.Black * hudVisibility);
GUI.DrawString(spriteBatch, pos - new Vector2(0.0f, size.Y / 2),
HighlightedLocation.Name, GUI.Style.TextColor * hudVisibility * 1.5f, font: GUI.LargeFont);
GUI.DrawString(spriteBatch, pos + new Vector2(0.0f, size.Y / 2 - GUI.Font.MeasureString(HighlightedLocation.Type.Name).Y),
HighlightedLocation.Type.Name, GUI.Style.TextColor * hudVisibility * 1.5f);
spriteBatch, new Rectangle((int)(pos.X - 60 * GUI.Scale), (int)(pos.Y - size.Y), (int)(size.X + 120 * GUI.Scale), (int)(size.Y * 2.2f)), Color.Black * hudVisibility);
var topLeftPos = pos - new Vector2(0.0f, size.Y / 2);
GUI.DrawString(spriteBatch, topLeftPos, HighlightedLocation.Name, GUI.Style.TextColor * hudVisibility * 1.5f, font: GUI.LargeFont);
topLeftPos += new Vector2(0.0f, nameSize.Y);
GUI.DrawString(spriteBatch, topLeftPos, HighlightedLocation.Type.Name, GUI.Style.TextColor * hudVisibility * 1.5f);
if (showReputation)
{
topLeftPos += new Vector2(0.0f, typeSize.Y + repLabelSize.Y);
GUI.DrawString(spriteBatch, topLeftPos, repLabelText, GUI.Style.TextColor * hudVisibility * 1.5f);
topLeftPos += new Vector2(0.0f, repLabelSize.Y + 4);
Rectangle repBarRect = new Rectangle(new Point((int)topLeftPos.X, (int)topLeftPos.Y), new Point((int)repBarSize.X, (int)repBarSize.Y));
RoundSummary.DrawReputationBar(spriteBatch, repBarRect, HighlightedLocation.Reputation.NormalizedValue);
GUI.DrawString(spriteBatch, new Vector2(repBarRect.Right + 4, repBarRect.Top), repValueText, GUI.Style.TextColor);
}
}
if (tooltip != null)
{

View File

@@ -159,6 +159,31 @@ namespace Barotrauma
if (PlayerInput.IsCtrlDown())
{
#if DEBUG
if (PlayerInput.KeyHit(Keys.D))
{
bool terminate = false;
foreach (MapEntity entity in selectedList)
{
if (entity is Item item && item.GetComponent<Planter>() is { } planter)
{
planter.Update(1.0f, cam);
for (var i = 0; i < planter.GrowableSeeds.Length; i++)
{
Growable seed = planter.GrowableSeeds[i];
PlantSlot slot = planter.PlantSlots.ContainsKey(i) ? planter.PlantSlots[i] : Planter.NullSlot;
if (seed == null) { continue; }
seed.CreateDebugHUD(planter, slot);
terminate = true;
break;
}
}
if (terminate) { break; }
}
}
#endif
if (PlayerInput.KeyHit(Keys.C))
{
Copy(selectedList);
@@ -897,8 +922,9 @@ namespace Barotrauma
Clone(copiedList);
var clones = mapEntityList.Except(prevEntities).ToList();
var nonWireClones = clones.Where(c => !(c is Item item) || item.GetComponent<Wire>() == null);
if (!nonWireClones.Any()) { nonWireClones = clones; }
Vector2 center = Vector2.Zero;
nonWireClones.ForEach(c => center += c.WorldPosition);
center = Submarine.VectorToWorldGrid(center / nonWireClones.Count());

View File

@@ -185,9 +185,20 @@ namespace Barotrauma
public override bool IsVisible(Rectangle worldView)
{
Rectangle worldRect = WorldRect;
Vector2 worldPos = WorldPosition;
if (worldRect.X > worldView.Right || worldRect.Right < worldView.X) { return false; }
if (worldRect.Y < worldView.Y - worldView.Height || worldRect.Y - worldRect.Height > worldView.Y) { return false; }
Vector2 min = new Vector2(worldRect.X, worldRect.Y - worldRect.Height);
Vector2 max = new Vector2(worldRect.Right, worldRect.Y);
foreach (DecorativeSprite decorativeSprite in Prefab.DecorativeSprites)
{
min.X = Math.Min(worldPos.X - decorativeSprite.Sprite.size.X * decorativeSprite.Sprite.RelativeOrigin.X * decorativeSprite.Scale * Scale, min.X);
max.X = Math.Max(worldPos.X + decorativeSprite.Sprite.size.X * (1.0f - decorativeSprite.Sprite.RelativeOrigin.X) * decorativeSprite.Scale * Scale, max.X);
min.Y = Math.Min(worldPos.Y - decorativeSprite.Sprite.size.Y * (1.0f - decorativeSprite.Sprite.RelativeOrigin.Y) * decorativeSprite.Scale * Scale, min.Y);
max.Y = Math.Max(worldPos.Y + decorativeSprite.Sprite.size.Y * decorativeSprite.Sprite.RelativeOrigin.Y * decorativeSprite.Scale * Scale, max.Y);
}
if (min.X > worldView.Right || max.X < worldView.X) { return false; }
if ( min.Y > worldView.Y || max.Y < worldView.Y - worldView.Height) { return false; }
return true;
}
@@ -455,8 +466,16 @@ namespace Barotrauma
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
byte sectionCount = msg.ReadByte();
if (sectionCount != Sections.Length)
bool invalidMessage = false;
if (type != ServerNetObject.ENTITY_EVENT && type != ServerNetObject.ENTITY_EVENT_INITIAL)
{
DebugConsole.NewMessage($"Error while reading a network event for the structure \"{Name} ({ID})\". Invalid event type ({type}).", Color.Red);
return;
}
else if (sectionCount != Sections.Length)
{
invalidMessage = true;
string errorMsg = $"Error while reading a network event for the structure \"{Name} ({ID})\". Section count does not match (server: {sectionCount} client: {Sections.Length})";
DebugConsole.NewMessage(errorMsg, Color.Red);
GameAnalyticsManager.AddErrorEventOnce("Structure.ClientRead:SectionCountMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
@@ -465,7 +484,7 @@ namespace Barotrauma
for (int i = 0; i < sectionCount; i++)
{
float damage = msg.ReadRangedSingle(0.0f, 1.0f, 8) * MaxHealth;
if (i < Sections.Length)
if (!invalidMessage && i < Sections.Length)
{
SetDamage(i, damage);
}

View File

@@ -11,6 +11,7 @@ using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Items.Components;
using System.Globalization;
namespace Barotrauma
{
@@ -19,6 +20,7 @@ namespace Barotrauma
public Sound Sound;
public readonly float Volume;
public readonly float Range;
public readonly Vector2 FrequencyMultiplierRange;
public readonly bool Stream;
public string Filename
@@ -32,8 +34,34 @@ namespace Barotrauma
Stream = sound.Stream;
Range = element.GetAttributeFloat("range", 1000.0f);
Volume = element.GetAttributeFloat("volume", 1.0f);
FrequencyMultiplierRange = new Vector2(1.0f);
string freqMultAttr = element.GetAttributeString("frequencymultiplier", element.GetAttributeString("frequency", "1.0"));
if (!freqMultAttr.Contains(','))
{
if (float.TryParse(freqMultAttr, NumberStyles.Any, CultureInfo.InvariantCulture, out float freqMult))
{
FrequencyMultiplierRange = new Vector2(freqMult);
}
}
else
{
var freqMult = XMLExtensions.ParseVector2(freqMultAttr, false);
if (freqMult.Y >= 0.25f)
{
FrequencyMultiplierRange = freqMult;
}
}
if (FrequencyMultiplierRange.Y > 4.0f)
{
DebugConsole.ThrowError($"Loaded frequency range exceeds max value: {FrequencyMultiplierRange} (original string was \"{freqMultAttr}\")");
}
sound.IgnoreMuffling = element.GetAttributeBool("dontmuffle", false);
}
public float GetRandomFrequencyMultiplier()
{
return Rand.Range(FrequencyMultiplierRange.X, FrequencyMultiplierRange.Y);
}
}
partial class Submarine : Entity, IServerSerializable
@@ -288,6 +316,27 @@ namespace Barotrauma
}
}
public static void DrawPaintedColors(SpriteBatch spriteBatch, bool editing = false, Predicate<MapEntity> predicate = null)
{
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.mapEntityList;
foreach (MapEntity e in entitiesToRender)
{
if (e is Hull hull)
{
if (hull.SupportsPaintedColors)
{
if (predicate != null)
{
if (!predicate(e)) continue;
}
hull.DrawSectionColors(spriteBatch);
}
}
}
}
public static void DrawBack(SpriteBatch spriteBatch, bool editing = false, Predicate<MapEntity> predicate = null)
{
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.mapEntityList;
@@ -511,6 +560,11 @@ namespace Barotrauma
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (type != ServerNetObject.ENTITY_POSITION)
{
DebugConsole.NewMessage($"Error while reading a network event for the submarine \"{Info.Name} ({ID})\". Invalid event type ({type}).", Color.Red);
}
var posInfo = PhysicsBody.ClientRead(type, msg, sendingTime, parentDebugName: Info.Name);
msg.ReadPadBits();

View File

@@ -238,42 +238,7 @@ namespace Barotrauma
return true;
}
private bool EnterIDCardDesc(GUITextBox textBox, string text)
{
IdCardDesc = text;
textBox.Text = text;
textBox.Color = GUI.Style.Green;
textBox.Deselect();
return true;
}
private bool EnterIDCardTags(GUITextBox textBox, string text)
{
IdCardTags = text.Split(',');
textBox.Text = string.Join(",", IdCardTags);
textBox.Flash(GUI.Style.Green);
textBox.Deselect();
return true;
}
private bool EnterTags(GUITextBox textBox, string text)
{
tags = text.Split(',').ToList();
textBox.Text = string.Join(",", Tags);
textBox.Flash(GUI.Style.Green);
textBox.Deselect();
return true;
}
private bool TextBoxChanged(GUITextBox textBox, string text)
{
textBox.Color = GUI.Style.Red;
return true;
}
private GUIComponent CreateEditingHUD(bool inGame = false)
private GUIComponent CreateEditingHUD()
{
int width = 500;
int height = spawnType == SpawnType.Path ? 80 : 200;
@@ -326,21 +291,48 @@ namespace Barotrauma
GUITextBox propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), descText.RectTransform, Anchor.CenterRight), IdCardDesc)
{
MaxTextLength = 150,
OnEnterPressed = EnterIDCardDesc,
ToolTip = TextManager.Get("IDCardDescriptionTooltip")
};
propertyBox.OnTextChanged += TextBoxChanged;
propertyBox.OnTextChanged += (textBox, text) =>
{
IdCardDesc = text;
return true;
};
propertyBox.OnEnterPressed += (textBox, text) =>
{
IdCardDesc = text;
textBox.Flash(GUI.Style.Green);
return true;
};
propertyBox.OnDeselected += (textBox, keys) =>
{
IdCardDesc = textBox.Text;
textBox.Flash(GUI.Style.Green);
};
var idCardTagsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
TextManager.Get("IDCardTags"), font: GUI.SmallFont);
propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), idCardTagsText.RectTransform, Anchor.CenterRight), string.Join(", ", idCardTags))
{
MaxTextLength = 60,
OnEnterPressed = EnterIDCardTags,
ToolTip = TextManager.Get("IDCardTagsTooltip")
};
propertyBox.OnTextChanged += TextBoxChanged;
propertyBox.OnTextChanged += (textBox, text) =>
{
IdCardTags = text.Split(',');
return true;
};
propertyBox.OnEnterPressed += (textBox, text) =>
{
textBox.Text = string.Join(",", IdCardTags);
textBox.Flash(GUI.Style.Green);
return true;
};
propertyBox.OnDeselected += (textBox, keys) =>
{
textBox.Text = string.Join(",", IdCardTags);
textBox.Flash(GUI.Style.Green);
};
var jobsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
TextManager.Get("SpawnpointJobs"), font: GUI.SmallFont)
@@ -368,12 +360,26 @@ namespace Barotrauma
propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), tagsText.RectTransform, Anchor.CenterRight), string.Join(", ", tags))
{
MaxTextLength = 60,
OnEnterPressed = EnterTags,
ToolTip = TextManager.Get("spawnpointtagstooltip")
};
propertyBox.OnTextChanged += TextBoxChanged;
propertyBox.OnTextChanged += (textBox, text) =>
{
tags = text.Split(',').ToList();
return true;
};
propertyBox.OnEnterPressed += (textBox, text) =>
{
textBox.Text = string.Join(",", tags);
textBox.Flash(GUI.Style.Green);
return true;
};
propertyBox.OnDeselected += (textBox, keys) =>
{
textBox.Text = string.Join(",", tags);
textBox.Flash(GUI.Style.Green);
};
}
PositionEditingHUD();
return editingHUD;

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -7,12 +8,13 @@ namespace Barotrauma.Networking
{
partial class BannedPlayer
{
public BannedPlayer(string name, UInt16 uniqueIdentifier, bool isRangeBan, string ip, ulong steamID)
public BannedPlayer(string name, UInt16 uniqueIdentifier, bool isRangeBan, string endPoint, ulong steamID)
{
this.Name = name;
this.EndPoint = endPoint;
this.SteamID = steamID;
ParseEndPointAsSteamId();
this.IsRangeBan = isRangeBan;
this.IP = ip;
this.UniqueIdentifier = uniqueIdentifier;
}
}
@@ -66,13 +68,13 @@ namespace Barotrauma.Networking
RelativeSpacing = 0.02f
};
string ip = bannedPlayer.IP;
if (localRangeBans.Contains(bannedPlayer.UniqueIdentifier)) ip = ToRange(ip);
string endPoint = bannedPlayer.EndPoint;
if (localRangeBans.Contains(bannedPlayer.UniqueIdentifier)) endPoint = ToRange(endPoint);
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), topArea.RectTransform),
bannedPlayer.Name + " (" + ip + ")");
bannedPlayer.Name + " (" + endPoint + ")");
textBlock.RectTransform.MinSize = new Point(textBlock.Rect.Width, 0);
if (bannedPlayer.IP.IndexOf(".x") <= -1)
if (bannedPlayer.EndPoint.IndexOf(".x") <= -1)
{
var rangeBanButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.4f), topArea.RectTransform),
TextManager.Get("BanRange"), style: "GUIButtonSmall")
@@ -156,19 +158,19 @@ namespace Barotrauma.Networking
UInt16 uniqueIdentifier = incMsg.ReadUInt16();
bool isRangeBan = incMsg.ReadBoolean(); incMsg.ReadPadBits();
string ip = "";
string endPoint = "";
UInt64 steamID = 0;
if (isOwner)
{
ip = incMsg.ReadString();
endPoint = incMsg.ReadString();
steamID = incMsg.ReadUInt64();
}
else
{
ip = "IP concealed by host";
endPoint = "Endpoint concealed by host";
steamID = 0;
}
bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, ip, steamID));
bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, endPoint, steamID));
}
if (banFrame != null)

View File

@@ -520,11 +520,13 @@ namespace Barotrauma.Networking
msgBox.Content.Parent.RectTransform.MinSize = new Point(0, (int)(msgBox.Content.RectTransform.MinSize.Y / msgBox.Content.RectTransform.RelativeSize.Y));
var okButton = msgBox.Buttons[0];
okButton.OnClicked += msgBox.Close;
var cancelButton = msgBox.Buttons[1];
cancelButton.OnClicked += msgBox.Close;
okButton.OnClicked += (GUIButton button, object obj) =>
{
clientPeer.SendPassword(passwordBox.Text);
clientPeer?.SendPassword(passwordBox.Text);
requiresPw = false;
return true;
};
@@ -855,22 +857,6 @@ namespace Barotrauma.Networking
traitorResults.Add(new TraitorMissionResult(inc));
}
if (GameMain.GameSession?.GameMode is CampaignMode mpCampaign)
{
if (inc.ReadBoolean())
{
Dictionary<string, int> clientUpgrades = UpgradeManager.GetMetadataLevels(mpCampaign.CampaignMetadata);
Dictionary<string, int> serverUpgrades = new Dictionary<string, int>();
int length = inc.ReadUInt16();
for (int i = 0; i < length; i++)
{
serverUpgrades.Add(inc.ReadString(), inc.ReadByte());
}
UpgradeManager.CompareUpgrades(clientUpgrades, serverUpgrades);
}
}
roundInitStatus = RoundInitStatus.Interrupted;
CoroutineManager.StartCoroutine(EndGame(endMessage, traitorResults, transitionType), "EndGame");
break;
@@ -934,7 +920,7 @@ namespace Barotrauma.Networking
private void ReadStartGameFinalize(IReadMessage inc)
{
TaskPool.ListTasks(null);
TaskPool.ListTasks();
ushort contentToPreloadCount = inc.ReadUInt16();
List<ContentFile> contentToPreload = new List<ContentFile>();
for (int i = 0; i < contentToPreloadCount; i++)
@@ -1006,8 +992,19 @@ namespace Barotrauma.Networking
}
private void OnDisconnect()
private void OnDisconnect(bool disableReconnect)
{
CoroutineManager.StopCoroutines("WaitForStartingInfo");
reconnectBox?.Close();
reconnectBox = null;
GameMain.Config.RestoreBackupPackages();
GUI.ClearCursorWait();
if (disableReconnect) { allowReconnect = false; }
if (!this.allowReconnect) { CancelConnect(); }
if (SteamManager.IsInitialized)
{
Steamworks.SteamFriends.ClearRichPresence();
@@ -1107,8 +1104,9 @@ namespace Barotrauma.Networking
reconnectBox?.Close();
reconnectBox = new GUIMessageBox(
TextManager.Get("ConnectionLost"),
msg, new string[0]);
TextManager.Get("ConnectionLost"), msg,
new string[] { TextManager.Get("Cancel") });
reconnectBox.Buttons[0].OnClicked += (btn, userdata) => { CancelConnect(); return true; };
connected = false;
ConnectToServer(serverEndpoint, serverName);
}
@@ -1385,6 +1383,7 @@ namespace Barotrauma.Networking
if (gameMode == null)
{
DebugConsole.ThrowError("Game mode \"" + modeIdentifier + "\" not found!");
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
@@ -1415,11 +1414,13 @@ namespace Barotrauma.Networking
int missionIndex = inc.ReadInt16();
if (!GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, GameMain.NetLobbyScreen.SubList))
{
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Success;
}
if (!GameMain.NetLobbyScreen.TrySelectSub(shuttleName, shuttleHash, GameMain.NetLobbyScreen.ShuttleList.ListBox))
{
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Success;
}
@@ -1448,6 +1449,7 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.Select();
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:FailedToSelectSub" + subName, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
if (GameMain.NetLobbyScreen.SelectedShuttle == null ||
@@ -1459,6 +1461,7 @@ namespace Barotrauma.Networking
string errorMsg = "Failed to select shuttle \"" + shuttleName + "\" (hash: " + shuttleHash + ").";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:FailedToSelectShuttle" + shuttleName, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
@@ -1487,6 +1490,7 @@ namespace Barotrauma.Networking
gameStarted = true;
DebugConsole.ThrowError(errorMsg);
GameMain.NetLobbyScreen.Select();
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
else if (campaign.Map == null)
@@ -1495,6 +1499,7 @@ namespace Barotrauma.Networking
gameStarted = true;
DebugConsole.ThrowError(errorMsg);
GameMain.NetLobbyScreen.Select();
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
@@ -1515,6 +1520,11 @@ namespace Barotrauma.Networking
}
}
if (GameMain.Client?.ServerSettings?.Voting != null)
{
GameMain.Client.ServerSettings.Voting.ResetVotes(GameMain.Client.ConnectedClients);
}
if (loadTask != null)
{
while (!loadTask.IsCompleted && !loadTask.IsFaulted && !loadTask.IsCanceled)
@@ -1626,7 +1636,10 @@ namespace Barotrauma.Networking
}
}
if (respawnAllowed) { respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle ? GameMain.NetLobbyScreen.SelectedShuttle : null); }
if (respawnAllowed)
{
respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle && gameMode != GameModePreset.MultiPlayerCampaign ? GameMain.NetLobbyScreen.SelectedShuttle : null);
}
gameStarted = true;
ServerSettings.ServerDetailsChanged = true;
@@ -1645,6 +1658,17 @@ namespace Barotrauma.Networking
public IEnumerable<object> EndGame(string endMessage, List<TraitorMissionResult> traitorResults = null, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
{
//round starting up, wait for it to finish
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 60);
while (TaskPool.IsTaskRunning("AsyncCampaignStartRound"))
{
if (DateTime.Now > timeOut)
{
throw new Exception("Failed to end a round (async campaign round start timed out).");
}
yield return new WaitForSeconds(1.0f);
}
if (!gameStarted)
{
GameMain.NetLobbyScreen.Select();
@@ -2086,7 +2110,6 @@ namespace Barotrauma.Networking
{
while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE)
{
bool eventReadFailed = false;
switch (objHeader)
{
case ServerNetObject.SYNC_IDS:
@@ -2110,7 +2133,13 @@ namespace Barotrauma.Networking
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
var entity = Entity.FindEntityByID(id) as IServerSerializable;
if (entity != null)
if (msgEndPos > inc.LengthBits)
{
DebugConsole.ThrowError($"Error while reading a position update for the entity \"({entity?.ToString() ?? "null"})\". Message length exceeds the size of the buffer.");
return;
}
if (entity != null && (entity is Item || entity is Character || entity is Submarine))
{
entity.ClientRead(objHeader.Value, inc, sendingTime);
}
@@ -2127,8 +2156,7 @@ namespace Barotrauma.Networking
case ServerNetObject.ENTITY_EVENT_INITIAL:
if (!entityEventManager.Read(objHeader.Value, inc, sendingTime, entities))
{
eventReadFailed = true;
break;
return;
}
break;
case ServerNetObject.CHAT_MESSAGE:
@@ -2138,16 +2166,11 @@ namespace Barotrauma.Networking
throw new Exception($"Unknown object header \"{objHeader}\"!)");
}
prevBitLength = inc.BitPosition - prevBitPos;
prevByteLength = inc.BytePosition - prevByteLength;
prevByteLength = inc.BytePosition - prevBytePos;
prevObjHeader = objHeader;
prevBitPos = inc.BitPosition;
prevBytePos = inc.BytePosition;
if (eventReadFailed)
{
break;
}
}
}
@@ -2618,7 +2641,7 @@ namespace Barotrauma.Networking
public void VoteForKick(Client votedClient)
{
if (votedClient == null) { return; }
votedClient.AddKickVote(ConnectedClients.First(c => c.ID == ID));
votedClient.AddKickVote(ConnectedClients.FirstOrDefault(c => c.ID == myID));
Vote(VoteType.Kick, votedClient);
}

View File

@@ -164,6 +164,19 @@ namespace Barotrauma.Networking
for (int i = 0; i < eventCount; i++)
{
//16 = entity ID, 8 = msg length
if (msg.BitPosition + 16 + 8 > msg.LengthBits)
{
string errorMsg = $"Error while reading a message from the server. Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits}).";
errorMsg += "\nPrevious entities:";
for (int j = entities.Count - 1; j >= 0; j--)
{
errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
}
DebugConsole.ThrowError(errorMsg);
return false;
}
UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i);
UInt16 entityID = msg.ReadUInt16();
@@ -176,7 +189,7 @@ namespace Barotrauma.Networking
}
msg.ReadPadBits();
entities.Add(null);
if (thisEventID == (UInt16)(lastReceivedID + 1)) lastReceivedID++;
if (thisEventID == (UInt16)(lastReceivedID + 1)) { lastReceivedID++; }
continue;
}
@@ -248,10 +261,8 @@ namespace Barotrauma.Networking
errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
}
if (GameSettings.VerboseLogging)
{
DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
}
DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
msg.BitPosition = (int)(msgPosition + msgLength * 8);

View File

@@ -1,13 +1,56 @@
using System;
using Barotrauma.Extensions;
using Barotrauma.Steam;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Barotrauma.Networking
{
abstract class ClientPeer
{
protected class ServerContentPackage
{
public string Name;
public string Hash;
public UInt64 WorkshopId;
public ContentPackage RegularPackage
{
get
{
return ContentPackage.RegularPackages.Find(p => p.MD5hash.Hash.Equals(Hash));
}
}
public ContentPackage CorePackage
{
get
{
return ContentPackage.CorePackages.Find(p => p.MD5hash.Hash.Equals(Hash));
}
}
public ServerContentPackage(string name, string hash, UInt64 workshopId)
{
Name = name;
Hash = hash;
WorkshopId = workshopId;
}
}
protected string GetPackageStr(ContentPackage contentPackage)
{
return "\"" + contentPackage.Name + "\" (hash " + contentPackage.MD5hash.ShortHash + ")";
}
protected string GetPackageStr(ServerContentPackage contentPackage)
{
return "\"" + contentPackage.Name + "\" (hash " + Md5Hash.GetShortHash(contentPackage.Hash) + ")";
}
public delegate void MessageCallback(IReadMessage message);
public delegate void DisconnectCallback();
public delegate void DisconnectCallback(bool disableReconnect);
public delegate void DisconnectMessageCallback(string message);
public delegate void PasswordCallback(int salt, int retries);
public delegate void InitializationCompleteCallback();
@@ -25,11 +68,150 @@ namespace Barotrauma.Networking
public NetworkConnection ServerConnection { get; protected set; }
public abstract void Start(object endPoint, int ownerKey);
public abstract void Close(string msg = null);
public abstract void Close(string msg = null, bool disableReconnect = false);
public abstract void Update(float deltaTime);
public abstract void Send(IWriteMessage msg, DeliveryMethod deliveryMethod);
public abstract void SendPassword(string password);
protected abstract void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg);
protected ConnectionInitialization initializationStep;
protected bool contentPackageOrderReceived;
protected int ownerKey = 0;
protected int passwordSalt;
protected Steamworks.AuthTicket steamAuthTicket;
protected void ReadConnectionInitializationStep(IReadMessage inc)
{
ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();
IWriteMessage outMsg;
switch (step)
{
case ConnectionInitialization.SteamTicketAndVersion:
if (initializationStep != ConnectionInitialization.SteamTicketAndVersion) { return; }
outMsg = new WriteOnlyMessage();
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.SteamTicketAndVersion);
outMsg.Write(Name);
outMsg.Write(ownerKey);
outMsg.Write(SteamManager.GetSteamID());
if (steamAuthTicket == null)
{
outMsg.Write((UInt16)0);
}
else
{
outMsg.Write((UInt16)steamAuthTicket.Data.Length);
outMsg.Write(steamAuthTicket.Data, 0, steamAuthTicket.Data.Length);
}
outMsg.Write(GameMain.Version.ToString());
outMsg.Write(GameMain.Config.Language);
SendMsgInternal(DeliveryMethod.Reliable, outMsg);
break;
case ConnectionInitialization.ContentPackageOrder:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion ||
initializationStep == ConnectionInitialization.Password) { initializationStep = ConnectionInitialization.ContentPackageOrder; }
if (initializationStep != ConnectionInitialization.ContentPackageOrder) { return; }
outMsg = new WriteOnlyMessage();
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.ContentPackageOrder);
string serverName = inc.ReadString();
UInt32 cpCount = inc.ReadVariableUInt32();
ServerContentPackage corePackage = null;
List<ServerContentPackage> regularPackages = new List<ServerContentPackage>();
List<ServerContentPackage> missingPackages = new List<ServerContentPackage>();
for (int i = 0; i < cpCount; i++)
{
string name = inc.ReadString();
string hash = inc.ReadString();
UInt64 workshopId = inc.ReadUInt64();
var pkg = new ServerContentPackage(name, hash, workshopId);
if (pkg.CorePackage != null)
{
corePackage = pkg;
}
else if (pkg.RegularPackage != null)
{
regularPackages.Add(pkg);
}
else
{
missingPackages.Add(pkg);
}
}
if (missingPackages.Count > 0)
{
var nonDownloadable = missingPackages.Where(p => p.WorkshopId == 0);
if (nonDownloadable.Any())
{
string disconnectMsg;
if (nonDownloadable.Count() == 1)
{
disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}";
}
else
{
List<string> packageStrs = new List<string>();
nonDownloadable.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
disconnectMsg = $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}";
}
Close(disconnectMsg, disableReconnect: true);
}
else
{
Close(disableReconnect: true);
string missingModNames = "\n- " + string.Join("\n\n- ", missingPackages.Select(p => GetPackageStr(p))) + "\n\n";
var msgBox = new GUIMessageBox(
TextManager.Get("WorkshopItemDownloadTitle"),
TextManager.GetWithVariable("WorkshopItemDownloadPrompt", "[items]", missingModNames),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
msgBox.Buttons[0].OnClicked = (yesBtn, userdata) =>
{
GameMain.ServerListScreen.Select();
GameMain.ServerListScreen.DownloadWorkshopItems(missingPackages.Select(p => p.WorkshopId), serverName, ServerConnection.EndPointString);
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
return;
}
if (!contentPackageOrderReceived)
{
GameMain.Config.SwapPackages(corePackage.CorePackage, regularPackages.Select(p => p.RegularPackage).ToList());
contentPackageOrderReceived = true;
}
SendMsgInternal(DeliveryMethod.Reliable, outMsg);
break;
case ConnectionInitialization.Password:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { initializationStep = ConnectionInitialization.Password; }
if (initializationStep != ConnectionInitialization.Password) { return; }
bool incomingSalt = inc.ReadBoolean(); inc.ReadPadBits();
int retries = 0;
if (incomingSalt)
{
passwordSalt = inc.ReadInt32();
}
else
{
retries = inc.ReadInt32();
}
OnRequestPassword?.Invoke(passwordSalt, retries);
break;
}
}
#if DEBUG
public abstract void ForceTimeOut();
#endif

View File

@@ -14,11 +14,6 @@ namespace Barotrauma.Networking
private NetClient netClient;
private NetPeerConfiguration netPeerConfiguration;
private ConnectionInitialization initializationStep;
private bool contentPackageOrderReceived;
private int ownerKey;
private int passwordSalt;
private Steamworks.AuthTicket steamAuthTicket;
List<NetIncomingMessage> incomingLidgrenMessages;
public LidgrenClientPeer(string name)
@@ -128,7 +123,7 @@ namespace Barotrauma.Networking
if (isConnectionInitializationStep && initializationStep != ConnectionInitialization.Success)
{
ReadConnectionInitializationStep(inc);
ReadConnectionInitializationStep(new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false));
}
else
{
@@ -158,99 +153,6 @@ namespace Barotrauma.Networking
}
}
private void ReadConnectionInitializationStep(NetIncomingMessage inc)
{
if (!isActive) { return; }
ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();
//Console.WriteLine(step + " " + initializationStep);
NetOutgoingMessage outMsg; NetSendResult result;
switch (step)
{
case ConnectionInitialization.SteamTicketAndVersion:
if (initializationStep != ConnectionInitialization.SteamTicketAndVersion) { return; }
outMsg = netClient.CreateMessage();
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.SteamTicketAndVersion);
outMsg.Write(Name);
outMsg.Write(ownerKey);
outMsg.Write(SteamManager.GetSteamID());
if (steamAuthTicket == null)
{
outMsg.Write((UInt16)0);
}
else
{
outMsg.Write((UInt16)steamAuthTicket.Data.Length);
outMsg.Write(steamAuthTicket.Data, 0, steamAuthTicket.Data.Length);
}
outMsg.Write(GameMain.Version.ToString());
IEnumerable<ContentPackage> mpContentPackages = GameMain.SelectedPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
outMsg.WriteVariableInt32(mpContentPackages.Count());
foreach (ContentPackage contentPackage in mpContentPackages)
{
outMsg.Write(contentPackage.Name);
outMsg.Write(contentPackage.MD5hash.Hash);
}
result = netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered);
if (result != NetSendResult.Queued && result != NetSendResult.Sent)
{
DebugConsole.NewMessage("Failed to send "+initializationStep.ToString()+" message to host: " + result);
}
break;
case ConnectionInitialization.ContentPackageOrder:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion ||
initializationStep == ConnectionInitialization.Password) { initializationStep = ConnectionInitialization.ContentPackageOrder; }
if (initializationStep != ConnectionInitialization.ContentPackageOrder) { return; }
outMsg = netClient.CreateMessage();
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.ContentPackageOrder);
Int32 cpCount = inc.ReadVariableInt32();
List<ContentPackage> serverContentPackages = new List<ContentPackage>();
for (int i = 0; i < cpCount; i++)
{
string hash = inc.ReadString();
serverContentPackages.Add(GameMain.Config.SelectedContentPackages.Find(cp => cp.MD5hash.Hash == hash));
}
if (!contentPackageOrderReceived)
{
GameMain.Config.ReorderSelectedContentPackages(cp => serverContentPackages.Contains(cp) ?
serverContentPackages.IndexOf(cp) :
serverContentPackages.Count + GameMain.Config.SelectedContentPackages.IndexOf(cp));
contentPackageOrderReceived = true;
}
result = netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered);
if (result != NetSendResult.Queued && result != NetSendResult.Sent)
{
DebugConsole.NewMessage("Failed to send " + initializationStep.ToString() + " message to host: " + result);
}
break;
case ConnectionInitialization.Password:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { initializationStep = ConnectionInitialization.Password; }
if (initializationStep != ConnectionInitialization.Password) { return; }
bool incomingSalt = inc.ReadBoolean(); inc.ReadPadBits();
int retries = 0;
if (incomingSalt)
{
passwordSalt = inc.ReadInt32();
}
else
{
retries = inc.ReadInt32();
}
OnRequestPassword?.Invoke(passwordSalt, retries);
break;
}
}
public override void SendPassword(string password)
{
if (!isActive) { return; }
@@ -269,7 +171,7 @@ namespace Barotrauma.Networking
}
}
public override void Close(string msg = null)
public override void Close(string msg = null, bool disableReconnect = false)
{
if (!isActive) { return; }
@@ -278,7 +180,7 @@ namespace Barotrauma.Networking
netClient.Shutdown(msg ?? TextManager.Get("Disconnecting"));
netClient = null;
steamAuthTicket?.Cancel(); steamAuthTicket = null;
OnDisconnect?.Invoke();
OnDisconnect?.Invoke(disableReconnect);
}
public override void Send(IWriteMessage msg, DeliveryMethod deliveryMethod)
@@ -320,6 +222,18 @@ namespace Barotrauma.Networking
}
}
protected override void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg)
{
NetOutgoingMessage lidgrenMsg = netClient.CreateMessage();
lidgrenMsg.Write(msg.Buffer, 0, msg.LengthBytes);
NetSendResult result = netClient.SendMessage(lidgrenMsg, NetDeliveryMethod.ReliableUnordered);
if (result != NetSendResult.Queued && result != NetSendResult.Sent)
{
DebugConsole.NewMessage("Failed to send message to host: " + result + "\n" + Environment.StackTrace);
}
}
#if DEBUG
public override void ForceTimeOut()
{

View File

@@ -12,10 +12,6 @@ namespace Barotrauma.Networking
{
private bool isActive;
private UInt64 hostSteamId;
private ConnectionInitialization initializationStep;
private bool contentPackageOrderReceived;
private int passwordSalt;
private Steamworks.AuthTicket steamAuthTicket;
private double timeout;
private double heartbeatTimer;
private double connectionStatusTimer;
@@ -89,6 +85,7 @@ namespace Barotrauma.Networking
Steamworks.SteamNetworking.AcceptP2PSessionWithUser(steamId);
}
else if (initializationStep != ConnectionInitialization.Password &&
initializationStep != ConnectionInitialization.ContentPackageOrder &&
initializationStep != ConnectionInitialization.Success)
{
DebugConsole.ThrowError($"Connection from incorrect SteamID was rejected: "+
@@ -105,7 +102,7 @@ namespace Barotrauma.Networking
OnDisconnectMessageReceived?.Invoke($"SteamP2P connection failed: {error}");
}
private void OnP2PData(ulong steamId, byte[] data, int dataLength, int channel)
private void OnP2PData(ulong steamId, byte[] data, int dataLength)
{
if (!isActive) { return; }
if (steamId != hostSteamId) { return; }
@@ -165,6 +162,7 @@ namespace Barotrauma.Networking
heartbeatTimer -= deltaTime;
if (initializationStep != ConnectionInitialization.Password &&
initializationStep != ConnectionInitialization.ContentPackageOrder &&
initializationStep != ConnectionInitialization.Success)
{
connectionStatusTimer -= deltaTime;
@@ -194,7 +192,7 @@ namespace Barotrauma.Networking
var packet = Steamworks.SteamNetworking.ReadP2PPacket();
if (packet.HasValue)
{
OnP2PData(packet?.SteamId ?? 0, packet?.Data, packet?.Data.Length ?? 0, 0);
OnP2PData(packet?.SteamId ?? 0, packet?.Data, packet?.Data.Length ?? 0);
receivedBytes += packet?.Data.Length ?? 0;
}
}
@@ -249,88 +247,6 @@ namespace Barotrauma.Networking
incomingDataMessages.Clear();
}
private void ReadConnectionInitializationStep(IReadMessage inc)
{
if (!isActive) { return; }
ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();
IWriteMessage outMsg;
//DebugConsole.NewMessage(step + " " + initializationStep);
switch (step)
{
case ConnectionInitialization.SteamTicketAndVersion:
if (initializationStep != ConnectionInitialization.SteamTicketAndVersion) { return; }
outMsg = new WriteOnlyMessage();
outMsg.Write((byte)DeliveryMethod.Reliable);
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.SteamTicketAndVersion);
outMsg.Write(Name);
outMsg.Write(SteamManager.GetSteamID());
outMsg.Write((UInt16)steamAuthTicket.Data.Length);
outMsg.Write(steamAuthTicket.Data, 0, steamAuthTicket.Data.Length);
outMsg.Write(GameMain.Version.ToString());
IEnumerable<ContentPackage> mpContentPackages = GameMain.SelectedPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count());
foreach (ContentPackage contentPackage in mpContentPackages)
{
outMsg.Write(contentPackage.Name);
outMsg.Write(contentPackage.MD5hash.Hash);
}
heartbeatTimer = 5.0;
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
break;
case ConnectionInitialization.ContentPackageOrder:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion ||
initializationStep == ConnectionInitialization.Password) { initializationStep = ConnectionInitialization.ContentPackageOrder; }
if (initializationStep != ConnectionInitialization.ContentPackageOrder) { return; }
outMsg = new WriteOnlyMessage();
outMsg.Write((byte)DeliveryMethod.Reliable);
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.ContentPackageOrder);
UInt32 cpCount = inc.ReadVariableUInt32();
List<ContentPackage> serverContentPackages = new List<ContentPackage>();
for (int i = 0; i < cpCount; i++)
{
string hash = inc.ReadString();
serverContentPackages.Add(GameMain.Config.SelectedContentPackages.Find(cp => cp.MD5hash.Hash == hash));
}
if (!contentPackageOrderReceived)
{
GameMain.Config.ReorderSelectedContentPackages(cp => serverContentPackages.Contains(cp) ?
serverContentPackages.IndexOf(cp) :
serverContentPackages.Count + GameMain.Config.SelectedContentPackages.IndexOf(cp));
contentPackageOrderReceived = true;
}
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
break;
case ConnectionInitialization.Password:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { initializationStep = ConnectionInitialization.Password; }
if (initializationStep != ConnectionInitialization.Password) { return; }
bool incomingSalt = inc.ReadBoolean(); inc.ReadPadBits();
int retries = 0;
if (incomingSalt)
{
passwordSalt = inc.ReadInt32();
}
else
{
retries = inc.ReadInt32();
}
OnRequestPassword?.Invoke(passwordSalt, retries);
break;
}
}
public override void Send(IWriteMessage msg, DeliveryMethod deliveryMethod)
{
if (!isActive) { return; }
@@ -339,8 +255,7 @@ namespace Barotrauma.Networking
buf[0] = (byte)deliveryMethod;
byte[] bufAux = new byte[msg.LengthBytes];
bool isCompressed; int length;
msg.PrepareForSending(ref bufAux, out isCompressed, out length);
msg.PrepareForSending(ref bufAux, out bool isCompressed, out int length);
buf[1] = (byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None);
@@ -426,7 +341,7 @@ namespace Barotrauma.Networking
sentBytes += outMsg.LengthBytes;
}
public override void Close(string msg = null)
public override void Close(string msg = null, bool disableReconnect = false)
{
if (!isActive) { return; }
@@ -451,7 +366,7 @@ namespace Barotrauma.Networking
steamAuthTicket?.Cancel(); steamAuthTicket = null;
hostSteamId = 0;
OnDisconnect?.Invoke();
OnDisconnect?.Invoke(disableReconnect);
}
~SteamP2PClientPeer()
@@ -460,6 +375,31 @@ namespace Barotrauma.Networking
Close();
}
protected override void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg)
{
Steamworks.P2PSend sendType;
switch (deliveryMethod)
{
case DeliveryMethod.Reliable:
case DeliveryMethod.ReliableOrdered:
//the documentation seems to suggest that the Reliable send type
//enforces packet order (TODO: verify)
sendType = Steamworks.P2PSend.Reliable;
break;
default:
sendType = Steamworks.P2PSend.Unreliable;
break;
}
IWriteMessage msgToSend = new WriteOnlyMessage();
msgToSend.Write((byte)deliveryMethod);
msgToSend.Write(msg.Buffer, 0, msg.LengthBytes);
heartbeatTimer = 5.0;
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, msgToSend.Buffer, msgToSend.LengthBytes, 0, sendType);
sentBytes += msg.LengthBytes;
}
#if DEBUG
public override void ForceTimeOut()
{

View File

@@ -1,8 +1,6 @@
using System;
using Barotrauma.Steam;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using Barotrauma.Steam;
using System.Linq;
using System.Threading;
@@ -12,7 +10,6 @@ namespace Barotrauma.Networking
{
private bool isActive;
private ConnectionInitialization initializationStep;
private readonly UInt64 selfSteamID;
private long sentBytes, receivedBytes;
@@ -61,7 +58,7 @@ namespace Barotrauma.Networking
initializationStep = ConnectionInitialization.SteamTicketAndVersion;
ServerConnection = new PipeConnection();
ServerConnection = new PipeConnection(selfSteamID);
ServerConnection.Status = NetworkConnectionStatus.Connected;
remotePeers = new List<RemotePeer>();
@@ -161,6 +158,7 @@ namespace Barotrauma.Networking
remotePeer.Authenticating = true;
authMsg.ReadString(); //skip name
authMsg.ReadInt32(); //skip owner key
authMsg.ReadUInt64(); //skip steamid
UInt16 ticketLength = authMsg.ReadUInt16();
byte[] ticket = authMsg.ReadBytes(ticketLength);
@@ -395,7 +393,7 @@ namespace Barotrauma.Networking
return; //owner doesn't send passwords
}
public override void Close(string msg = null)
public override void Close(string msg = null, bool disableReconnect = false)
{
if (!isActive) { return; }
@@ -415,7 +413,7 @@ namespace Barotrauma.Networking
ChildServerRelay.ClosePipes();
OnDisconnect?.Invoke();
OnDisconnect?.Invoke(disableReconnect);
SteamManager.LeaveLobby();
Steamworks.SteamNetworking.ResetActions();
@@ -445,6 +443,12 @@ namespace Barotrauma.Networking
Close();
}
protected override void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg)
{
//not currently used by SteamP2POwnerPeer
throw new NotImplementedException();
}
#if DEBUG
public override void ForceTimeOut()
{

View File

@@ -73,26 +73,25 @@ namespace Barotrauma.Networking
get;
private set;
} = new List<string>();
public List<string> ContentPackageWorkshopUrls
public List<ulong> ContentPackageWorkshopIds
{
get;
private set;
} = new List<string>();
} = new List<ulong>();
public bool ContentPackagesMatch(IEnumerable<ContentPackage> myContentPackages)
public bool ContentPackagesMatch()
{
var myContentPackages = ContentPackage.AllPackages;
//make sure we have all the packages the server requires
foreach (string hash in ContentPackageHashes)
if (ContentPackageHashes.Count != ContentPackageWorkshopIds.Count) { return false; }
for (int i = 0; i < ContentPackageWorkshopIds.Count; i++)
{
if (!myContentPackages.Any(myPackage => myPackage.MD5hash.Hash == hash)) { return false; }
}
//make sure the server isn't missing any of our packages that cause multiplayer incompatibility
foreach (ContentPackage myPackage in myContentPackages)
{
if (myPackage.HasMultiplayerIncompatibleContent)
string hash = ContentPackageHashes[i];
UInt64 id = ContentPackageWorkshopIds[i];
if (!myContentPackages.Any(myPackage => myPackage.MD5hash.Hash == hash))
{
if (!ContentPackageHashes.Any(hash => hash == myPackage.MD5hash.Hash)) { return false; }
if (myContentPackages.Any(p => p.SteamWorkshopId == id)) { return false; }
if (id == 0) { return false; }
}
}
@@ -145,19 +144,22 @@ namespace Barotrauma.Networking
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), previewContainer.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"), string.IsNullOrEmpty(GameVersion) ? TextManager.Get("Unknown") : GameVersion));
PlayStyle playStyle = PlayStyle ?? Networking.PlayStyle.Serious;
bool hidePlaystyleBanner = previewContainer.Rect.Height < 380 || !PlayStyle.HasValue;
if (!hidePlaystyleBanner)
{
PlayStyle playStyle = PlayStyle ?? Networking.PlayStyle.Serious;
Sprite playStyleBannerSprite = ServerListScreen.PlayStyleBanners[(int)playStyle];
float playStyleBannerAspectRatio = playStyleBannerSprite.SourceRect.Width / playStyleBannerSprite.SourceRect.Height;
var playStyleBanner = new GUIImage(new RectTransform(new Point(previewContainer.Rect.Width, (int)(previewContainer.Rect.Width / playStyleBannerAspectRatio)), previewContainer.RectTransform),
playStyleBannerSprite, null, true);
Sprite playStyleBannerSprite = ServerListScreen.PlayStyleBanners[(int)playStyle];
float playStyleBannerAspectRatio = playStyleBannerSprite.SourceRect.Width / playStyleBannerSprite.SourceRect.Height;
var playStyleBanner = new GUIImage(new RectTransform(new Point(previewContainer.Rect.Width, (int)(previewContainer.Rect.Width / playStyleBannerAspectRatio)), previewContainer.RectTransform),
playStyleBannerSprite, null, true);
var playStyleName = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.0f), playStyleBanner.RectTransform) { RelativeOffset = new Vector2(0.01f, 0.06f) },
TextManager.AddPunctuation(':', TextManager.Get("serverplaystyle"), TextManager.Get("servertag."+ playStyle)), textColor: Color.White,
font: GUI.SmallFont, textAlignment: Alignment.Center,
color: ServerListScreen.PlayStyleColors[(int)playStyle], style: "GUISlopedHeader");
playStyleName.RectTransform.NonScaledSize = (playStyleName.Font.MeasureString(playStyleName.Text) + new Vector2(20, 5) * GUI.Scale).ToPoint();
playStyleName.RectTransform.IsFixedSize = true;
var playStyleName = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.0f), playStyleBanner.RectTransform) { RelativeOffset = new Vector2(0.01f, 0.06f) },
TextManager.AddPunctuation(':', TextManager.Get("serverplaystyle"), TextManager.Get("servertag."+ playStyle)), textColor: Color.White,
font: GUI.SmallFont, textAlignment: Alignment.Center,
color: ServerListScreen.PlayStyleColors[(int)playStyle], style: "GUISlopedHeader");
playStyleName.RectTransform.NonScaledSize = (playStyleName.Font.MeasureString(playStyleName.Text) + new Vector2(20, 5) * GUI.Scale).ToPoint();
playStyleName.RectTransform.IsFixedSize = true;
}
var content = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), previewContainer.RectTransform))
{
@@ -207,8 +209,13 @@ namespace Barotrauma.Networking
TextManager.Get(string.IsNullOrEmpty(GameMode) ? "Unknown" : "GameMode." + GameMode, returnNull: true) ?? GameMode,
textAlignment: Alignment.Right);
/*var traitors = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), bodyContainer.RectTransform), TextManager.Get("Traitors"));
new GUITextBlock(new RectTransform(Vector2.One, traitors.RectTransform), TextManager.Get(!TraitorsEnabled.HasValue ? "Unknown" : TraitorsEnabled.Value.ToString()), textAlignment: Alignment.Right);*/
GUITextBlock playStyleText = null;
if (hidePlaystyleBanner && PlayStyle.HasValue)
{
PlayStyle playStyle = PlayStyle.Value;
playStyleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get("serverplaystyle"));
new GUITextBlock(new RectTransform(Vector2.One, playStyleText.RectTransform), TextManager.Get("servertag." + playStyle), textAlignment: Alignment.Right);
}
var subSelection = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get("ServerListSubSelection"));
new GUITextBlock(new RectTransform(Vector2.One, subSelection.RectTransform), TextManager.Get(!SubSelectionMode.HasValue ? "Unknown" : SubSelectionMode.Value.ToString()), textAlignment: Alignment.Right);
@@ -222,6 +229,10 @@ namespace Barotrauma.Networking
{
gameMode.Font = subSelection.Font = modeSelection.Font = GUI.SmallFont;
gameMode.GetChild<GUITextBlock>().Font = subSelection.GetChild<GUITextBlock>().Font = modeSelection.GetChild<GUITextBlock>().Font = GUI.SmallFont;
if (playStyleText != null)
{
playStyleText.Font = playStyleText.GetChild<GUITextBlock>().Font = GUI.SmallFont;
}
}
var allowSpectating = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), content.RectTransform), TextManager.Get("ServerListAllowSpectating"))
@@ -279,7 +290,6 @@ namespace Barotrauma.Networking
}
else
{
List<string> availableWorkshopUrls = new List<string>();
for (int i = 0; i < ContentPackageNames.Count; i++)
{
var packageText = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.15f), contentPackageList.Content.RectTransform) { MinSize = new Point(0, 15) },
@@ -289,22 +299,15 @@ namespace Barotrauma.Networking
};
if (i < ContentPackageHashes.Count)
{
if (GameMain.Config.SelectedContentPackages.Any(cp => cp.MD5hash.Hash == ContentPackageHashes[i]))
if (ContentPackage.AllPackages.Any(cp => cp.MD5hash.Hash == ContentPackageHashes[i]))
{
packageText.Selected = true;
continue;
}
//matching content package found, but it hasn't been enabled
if (ContentPackage.List.Any(cp => cp.MD5hash.Hash == ContentPackageHashes[i]))
{
packageText.TextColor = GUI.Style.Orange;
packageText.ToolTip = TextManager.GetWithVariable("ServerListContentPackageNotEnabled", "[contentpackage]", ContentPackageNames[i]);
}
//workshop download link found
else if (i < ContentPackageWorkshopUrls.Count && !string.IsNullOrEmpty(ContentPackageWorkshopUrls[i]))
if (i < ContentPackageWorkshopIds.Count && ContentPackageWorkshopIds[i] != 0)
{
availableWorkshopUrls.Add(ContentPackageWorkshopUrls[i]);
packageText.TextColor = Color.Yellow;
packageText.ToolTip = TextManager.GetWithVariable("ServerListIncompatibleContentPackageWorkshopAvailable", "[contentpackage]", ContentPackageNames[i]);
}
@@ -316,21 +319,6 @@ namespace Barotrauma.Networking
}
}
}
if (availableWorkshopUrls.Count > 0)
{
var workshopBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), content.RectTransform), TextManager.Get("ServerListSubscribeMissingPackages"))
{
ToolTip = TextManager.Get(SteamManager.IsInitialized ? "ServerListSubscribeMissingPackagesTooltip" : "ServerListSubscribeMissingPackagesTooltipNoSteam"),
Enabled = SteamManager.IsInitialized,
OnClicked = (btn, userdata) =>
{
GameMain.SteamWorkshopScreen.SubscribeToPackages(availableWorkshopUrls);
GameMain.SteamWorkshopScreen.Select();
return true;
}
};
workshopBtn.TextBlock.AutoScaleHorizontal = true;
}
}
// -----------------------------------------------------------------------------
@@ -391,7 +379,7 @@ namespace Barotrauma.Networking
if (bool.TryParse(element.GetAttributeString("UsingWhiteList", ""), out bool whitelistTemp)) { info.UsingWhiteList = whitelistTemp; }
if (Enum.TryParse(element.GetAttributeString("TraitorsEnabled", ""), out YesNoMaybe traitorsTemp)) { info.TraitorsEnabled = traitorsTemp; }
if (Enum.TryParse(element.GetAttributeString("SubSelectionMode", ""), out SelectionMode subSelectionTemp)) { info.SubSelectionMode = subSelectionTemp; }
if (Enum.TryParse(element.GetAttributeString("ModeSelectionMode", ""), out SelectionMode modeSelectionTemp)) { info.ModeSelectionMode = subSelectionTemp; }
if (Enum.TryParse(element.GetAttributeString("ModeSelectionMode", ""), out SelectionMode modeSelectionTemp)) { info.ModeSelectionMode = modeSelectionTemp; }
if (bool.TryParse(element.GetAttributeString("VoipEnabled", ""), out bool voipTemp)) { info.VoipEnabled = voipTemp; }
if (bool.TryParse(element.GetAttributeString("KarmaEnabled", ""), out bool karmaTemp)) { info.KarmaEnabled = karmaTemp; }
if (bool.TryParse(element.GetAttributeString("FriendlyFireEnabled", ""), out bool friendlyFireTemp)) { info.FriendlyFireEnabled = friendlyFireTemp; }

View File

@@ -477,10 +477,6 @@ namespace Barotrauma.Networking
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));
var endBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
TextManager.Get("ServerSettingsEndRoundWhenDestReached"));
GetPropertyData("EndRoundAtLevelEnd").AssignGUIComponent(endBox);
var endVoteBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
TextManager.Get("ServerSettingsEndRoundVoting"));
GetPropertyData("AllowEndVoting").AssignGUIComponent(endVoteBox);
@@ -569,6 +565,8 @@ namespace Barotrauma.Networking
};
slider.OnMoved(slider, slider.BarScroll);
var traitorsMinPlayerCount = CreateLabeledNumberInput(roundsTab, "ServerSettingsTraitorsMinPlayerCount", 1, 16, "ServerSettingsTraitorsMinPlayerCountToolTip");
GetPropertyData("TraitorsMinPlayerCount").AssignGUIComponent(traitorsMinPlayerCount);
var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton"));
GetPropertyData("AllowRagdollButton").AssignGUIComponent(ragdollButtonBox);
@@ -576,53 +574,6 @@ namespace Barotrauma.Networking
var disableBotConversationsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsDisableBotConversations"));
GetPropertyData("DisableBotConversations").AssignGUIComponent(disableBotConversationsBox);
/*var traitorRatioBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsUseTraitorRatio"));
CreateLabeledSlider(roundsTab, "", out slider, out sliderLabel);
var traitorRatioSlider = slider;
traitorRatioBox.OnSelected = (GUITickBox) =>
{
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
return true;
};
if (TraitorUseRatio)
{
traitorRatioSlider.Range = new Vector2(0.1f, 1.0f);
}
else
{
traitorRatioSlider.Range = new Vector2(1.0f, maxPlayers);
}
string traitorRatioLabel = TextManager.Get("ServerSettingsTraitorRatio") + " ";
string traitorCountLabel = TextManager.Get("ServerSettingsTraitorCount") + " ";
traitorRatioSlider.Range = new Vector2(0.1f, 1.0f);
traitorRatioSlider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
GUITextBlock traitorText = scrollBar.UserData as GUITextBlock;
if (traitorRatioBox.Selected)
{
scrollBar.Step = 0.01f;
scrollBar.Range = new Vector2(0.1f, 1.0f);
traitorText.Text = traitorRatioLabel + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 1.0f) + " %";
}
else
{
scrollBar.Step = 1f / (maxPlayers - 1);
scrollBar.Range = new Vector2(1.0f, maxPlayers);
traitorText.Text = traitorCountLabel + scrollBar.BarScrollValue;
}
return true;
};
GetPropertyData("TraitorUseRatio").AssignGUIComponent(traitorRatioBox);
GetPropertyData("TraitorRatio").AssignGUIComponent(traitorRatioSlider);
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
traitorRatioBox.OnSelected(traitorRatioBox);*/
var buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), roundsTab.RectTransform), isHorizontal: true)
{
Stretch = true,
@@ -919,7 +870,7 @@ namespace Barotrauma.Networking
slider.UserData = label;
}
private GUINumberInput CreateLabeledNumberInput(GUIComponent parent, string labelTag, int min, int max)
private GUINumberInput CreateLabeledNumberInput(GUIComponent parent, string labelTag, int min, int max, string toolTipTag = null)
{
var container = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), isHorizontal: true)
{
@@ -928,11 +879,15 @@ namespace Barotrauma.Networking
ToolTip = TextManager.Get(labelTag)
};
new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), container.RectTransform),
var label = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), container.RectTransform),
TextManager.Get(labelTag), textAlignment: Alignment.CenterLeft, font: GUI.SmallFont)
{
AutoScaleHorizontal = true
};
if (!string.IsNullOrEmpty(toolTipTag))
{
label.ToolTip = TextManager.Get(toolTipTag);
}
var input = new GUINumberInput(new RectTransform(new Vector2(0.3f, 1.0f), container.RectTransform), GUINumberInput.NumberType.Int)
{
MinValueInt = min,

View File

@@ -1,22 +1,18 @@
using Barotrauma.Networking;
using Barotrauma.IO;
using Barotrauma.Networking;
using RestSharp;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using RestSharp.Contrib;
using System.Xml.Linq;
using Color = Microsoft.Xna.Framework.Color;
using System.Runtime.InteropServices;
using NLog.Fluent;
namespace Barotrauma.Steam
{
static partial class SteamManager
{
private static Dictionary<Steamworks.Data.PublishedFileId, Task> modCopiesInProgress = new Dictionary<Steamworks.Data.PublishedFileId, Task>();
private static readonly Dictionary<Steamworks.Data.PublishedFileId, Task> modCopiesInProgress = new Dictionary<Steamworks.Data.PublishedFileId, Task>();
private static void InitializeProjectSpecific()
{
@@ -49,6 +45,9 @@ namespace Barotrauma.Steam
}
catch (Exception e)
{
#if !DEBUG
DebugConsole.ThrowError("SteamManager initialization threw an exception", e);
#endif
isInitialized = false;
initializationErrors.Add("SteamClientInitFailed");
}
@@ -85,28 +84,6 @@ namespace Barotrauma.Steam
}
}
private static void UpdateProjectSpecific(float deltaTime)
{
if (ugcSubscriptionTasks != null)
{
var ugcSubscriptionKeys = ugcSubscriptionTasks.Keys.ToList();
foreach (var key in ugcSubscriptionKeys)
{
var task = ugcSubscriptionTasks[key];
if (task.IsCompleted)
{
if (!task.IsCompletedSuccessfully)
{
DebugConsole.ThrowError("Failed to subscribe to a Steam Workshop item with id " + key.ToString() + ": TaskStatus = " + task.Status.ToString());
}
ugcSubscriptionTasks.Remove(key);
}
}
}
}
public static async Task InitRelayNetworkAccess()
{
if (!IsInitialized) { return; }
@@ -209,13 +186,13 @@ namespace Barotrauma.Steam
return;
}
var contentPackages = GameMain.Config.SelectedContentPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
var contentPackages = GameMain.Config.AllEnabledPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
currentLobby?.SetData("name", serverSettings.ServerName);
currentLobby?.SetData("playercount", (GameMain.Client?.ConnectedClients?.Count ?? 0).ToString());
currentLobby?.SetData("maxplayernum", serverSettings.MaxPlayers.ToString());
//currentLobby?.SetData("hostipaddress", lobbyIP);
string pingLocation = Steamworks.SteamNetworkingUtils.LocalPingLocation.ToString();
string pingLocation = Steamworks.SteamNetworkingUtils.LocalPingLocation?.ToString();
currentLobby?.SetData("pinglocation", pingLocation ?? "");
currentLobby?.SetData("lobbyowner", SteamIDUInt64ToString(GetSteamID()));
currentLobby?.SetData("haspassword", serverSettings.HasPassword.ToString());
@@ -225,7 +202,7 @@ namespace Barotrauma.Steam
currentLobby?.SetData("contentpackage", string.Join(",", contentPackages.Select(cp => cp.Name)));
currentLobby?.SetData("contentpackagehash", string.Join(",", contentPackages.Select(cp => cp.MD5hash.Hash)));
currentLobby?.SetData("contentpackageurl", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopUrl ?? "")));
currentLobby?.SetData("contentpackageid", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopId)));
currentLobby?.SetData("usingwhitelist", (serverSettings.Whitelist != null && serverSettings.Whitelist.Enabled).ToString());
currentLobby?.SetData("modeselectionmode", serverSettings.ModeSelectionMode.ToString());
currentLobby?.SetData("subselectionmode", serverSettings.SubSelectionMode.ToString());
@@ -280,9 +257,8 @@ namespace Barotrauma.Steam
{
if (!isInitialized) { return false; }
int doneTasks = 0;
Action taskDone = () =>
void taskDone()
{
doneTasks++;
if (doneTasks >= 2)
@@ -290,52 +266,64 @@ namespace Barotrauma.Steam
serverQueryFinished?.Invoke();
serverQueryFinished = null;
}
};
}
//TODO: find a better strategy to fetch all lobbies, this is gonna take forever if we actually have 10000 lobbies
Steamworks.Data.LobbyQuery lobbyQuery = Steamworks.SteamMatchmaking.CreateLobbyQuery().FilterDistanceWorldwide().WithMaxResults(10000);
Steamworks.Dispatch.OnDebugCallback = (callbackType, contents, isServer) =>
{
DebugConsole.NewMessage($"{callbackType}: " + contents, Color.Yellow);
};
TaskPool.Add("LobbyQueryRequest", lobbyQuery.RequestAsync(),
(t) =>
{
var lobbies = ((Task<Steamworks.Data.Lobby[]>)t).Result;
foreach (var lobby in lobbies)
{
if (string.IsNullOrEmpty(lobby.GetData("name"))) { continue; }
ServerInfo serverInfo = new ServerInfo();
serverInfo.ServerName = lobby.GetData("name");
serverInfo.OwnerID = SteamIDStringToUInt64(lobby.GetData("lobbyowner"));
serverInfo.LobbyID = lobby.Id;
bool.TryParse(lobby.GetData("haspassword"), out serverInfo.HasPassword);
serverInfo.PlayerCount = int.TryParse(lobby.GetData("playercount"), out int playerCount) ? playerCount : 0;
serverInfo.MaxPlayers = int.TryParse(lobby.GetData("maxplayernum"), out int maxPlayers) ? maxPlayers : 1;
serverInfo.RespondedToSteamQuery = true;
AssignLobbyDataToServerInfo(lobby, serverInfo);
addToServerList(serverInfo);
}
taskDone();
Steamworks.Dispatch.OnDebugCallback = null;
if (t.Status == TaskStatus.Faulted)
{
TaskPool.PrintTaskExceptions(t, "Failed to retrieve SteamP2P lobbies");
taskDone();
return;
}
var lobbies = ((Task<Steamworks.Data.Lobby[]>)t).Result;
if (lobbies != null)
{
foreach (var lobby in lobbies)
{
if (string.IsNullOrEmpty(lobby.GetData("name"))) { continue; }
ServerInfo serverInfo = new ServerInfo();
serverInfo.ServerName = lobby.GetData("name");
serverInfo.OwnerID = SteamIDStringToUInt64(lobby.GetData("lobbyowner"));
serverInfo.LobbyID = lobby.Id;
bool.TryParse(lobby.GetData("haspassword"), out serverInfo.HasPassword);
serverInfo.PlayerCount = int.TryParse(lobby.GetData("playercount"), out int playerCount) ? playerCount : 0;
serverInfo.MaxPlayers = int.TryParse(lobby.GetData("maxplayernum"), out int maxPlayers) ? maxPlayers : 1;
serverInfo.RespondedToSteamQuery = true;
AssignLobbyDataToServerInfo(lobby, serverInfo);
addToServerList(serverInfo);
}
}
taskDone();
});
Steamworks.ServerList.Internet serverQuery = new Steamworks.ServerList.Internet();
Action<Steamworks.Data.ServerInfo, bool> onServer = (Steamworks.Data.ServerInfo info, bool responsive) =>
void onServer(Steamworks.Data.ServerInfo info, bool responsive)
{
if (string.IsNullOrEmpty(info.Name)) { return; }
ServerInfo serverInfo = new ServerInfo();
serverInfo.ServerName = info.Name;
serverInfo.IP = info.Address.ToString();
serverInfo.Port = info.ConnectionPort.ToString();
serverInfo.PlayerCount = info.Players;
serverInfo.MaxPlayers = info.MaxPlayers;
serverInfo.RespondedToSteamQuery = responsive;
ServerInfo serverInfo = new ServerInfo
{
ServerName = info.Name,
HasPassword = info.Passworded,
IP = info.Address.ToString(),
Port = info.ConnectionPort.ToString(),
PlayerCount = info.Players,
MaxPlayers = info.MaxPlayers,
RespondedToSteamQuery = responsive
};
if (responsive)
{
@@ -344,11 +332,11 @@ namespace Barotrauma.Steam
{
if (t.Status == TaskStatus.Faulted)
{
TaskPool.PrintTaskExceptions(t, "Failed to retrieve rules for "+info.Name);
TaskPool.PrintTaskExceptions(t, "Failed to retrieve rules for " + info.Name);
return;
}
var rules = ((Task<Dictionary<string,string>>)t).Result;
var rules = ((Task<Dictionary<string, string>>)t).Result;
AssignServerRulesToServerInfo(rules, serverInfo);
CrossThread.RequestExecutionOnMainThread(() =>
@@ -365,7 +353,7 @@ namespace Barotrauma.Steam
});
}
};
}
serverQuery.OnResponsiveServer += (info) => onServer(info, true);
serverQuery.OnUnresponsiveServer += (info) => onServer(info, false);
@@ -393,11 +381,20 @@ namespace Barotrauma.Steam
serverInfo.ContentPackageNames.AddRange(lobby.GetData("contentpackage").Split(','));
serverInfo.ContentPackageHashes.AddRange(lobby.GetData("contentpackagehash").Split(','));
serverInfo.ContentPackageWorkshopUrls.AddRange(lobby.GetData("contentpackageurl").Split(','));
string workshopIdData = lobby.GetData("contentpackageid");
if (!string.IsNullOrEmpty(workshopIdData))
{
serverInfo.ContentPackageWorkshopIds.AddRange(ParseWorkshopIds(workshopIdData));
}
else
{
string[] workshopUrls = lobby.GetData("contentpackageurl").Split(',');
serverInfo.ContentPackageWorkshopIds.AddRange(WorkshopUrlsToIds(workshopUrls));
}
serverInfo.UsingWhiteList = getLobbyBool("usingwhitelist");
SelectionMode selectionMode;
if (Enum.TryParse(lobby.GetData("modeselectionmode"), out selectionMode)) { serverInfo.ModeSelectionMode = selectionMode; }
if (Enum.TryParse(lobby.GetData("modeselectionmode"), out SelectionMode selectionMode)) { serverInfo.ModeSelectionMode = selectionMode; }
if (Enum.TryParse(lobby.GetData("subselectionmode"), out selectionMode)) { serverInfo.SubSelectionMode = selectionMode; }
serverInfo.AllowSpectating = getLobbyBool("allowspectating");
@@ -412,11 +409,12 @@ namespace Barotrauma.Steam
if (Enum.TryParse(lobby.GetData("playstyle"), out PlayStyle playStyle)) serverInfo.PlayStyle = playStyle;
if (serverInfo.ContentPackageNames.Count != serverInfo.ContentPackageHashes.Count ||
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopUrls.Count)
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopIds.Count)
{
//invalid contentpackage info
serverInfo.ContentPackageNames.Clear();
serverInfo.ContentPackageHashes.Clear();
serverInfo.ContentPackageWorkshopIds.Clear();
}
string pingLocation = lobby.GetData("pinglocation");
@@ -449,10 +447,18 @@ namespace Barotrauma.Steam
serverInfo.ContentPackageNames.Clear();
serverInfo.ContentPackageHashes.Clear();
serverInfo.ContentPackageWorkshopUrls.Clear();
serverInfo.ContentPackageWorkshopIds.Clear();
if (rules.ContainsKey("contentpackage")) serverInfo.ContentPackageNames.AddRange(rules["contentpackage"].Split(','));
if (rules.ContainsKey("contentpackagehash")) serverInfo.ContentPackageHashes.AddRange(rules["contentpackagehash"].Split(','));
if (rules.ContainsKey("contentpackageurl")) serverInfo.ContentPackageWorkshopUrls.AddRange(rules["contentpackageurl"].Split(','));
if (rules.ContainsKey("contentpackageid"))
{
serverInfo.ContentPackageWorkshopIds.AddRange(ParseWorkshopIds(rules["contentpackageid"]));
}
else if (rules.ContainsKey("contentpackageurl"))
{
string[] workshopUrls = rules["contentpackageurl"].Split(',');
serverInfo.ContentPackageWorkshopIds.AddRange(WorkshopUrlsToIds(workshopUrls));
}
if (rules.ContainsKey("usingwhitelist")) serverInfo.UsingWhiteList = rules["usingwhitelist"] == "True";
if (rules.ContainsKey("modeselectionmode"))
@@ -479,37 +485,16 @@ namespace Barotrauma.Steam
}
if (serverInfo.ContentPackageNames.Count != serverInfo.ContentPackageHashes.Count ||
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopUrls.Count)
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopIds.Count)
{
//invalid contentpackage info
serverInfo.ContentPackageNames.Clear();
serverInfo.ContentPackageHashes.Clear();
serverInfo.ContentPackageWorkshopIds.Clear();
}
}
public static ulong GetWorkshopItemIDFromUrl(string url)
{
try
{
Uri uri = new Uri(url);
string idStr = HttpUtility.ParseQueryString(uri.Query).Get("id");
if (ulong.TryParse(idStr, out ulong id))
{
return id;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to get Workshop item ID from the url \"" + url + "\"!", e);
}
return 0;
}
#region Connecting to servers
//TODO: reimplement server list queries
#region Connecting to servers
private static Steamworks.AuthTicket currentTicket = null;
public static Steamworks.AuthTicket GetAuthSessionTicket()
{
@@ -545,9 +530,9 @@ namespace Barotrauma.Steam
Steamworks.SteamUser.EndAuthSession(clientSteamID);
}
#endregion
#endregion
#region Workshop
#region Workshop
public const string WorkshopItemPreviewImageFolder = "Workshop";
public const string PreviewImageName = "PreviewImage.png";
@@ -620,7 +605,8 @@ namespace Barotrauma.Steam
.WithLongDescription();
if (requireTags != null) query.WithTags(requireTags);
TaskPool.Add("GetPopularWorkshopItems", GetWorkshopItemsAsync(query, amount, (item) => !item.IsSubscribed), (task) => {
TaskPool.Add("GetPopularWorkshopItems", GetWorkshopItemsAsync(query, amount, (item) => !item.IsSubscribed), (task) =>
{
var entries = ((Task<List<Steamworks.Ugc.Item>>)task).Result;
//count the number of each unique tag
@@ -669,47 +655,82 @@ namespace Barotrauma.Steam
TaskPool.Add("GetPublishedWorkshopItems", GetWorkshopItemsAsync(query), (task) => { onItemsFound?.Invoke(((Task<List<Steamworks.Ugc.Item>>)task).Result); });
}
private static Dictionary<ulong, Task> ugcSubscriptionTasks;
private static readonly HashSet<ulong> pendingWorkshopSubscriptions = new HashSet<ulong>();
public static void SubscribeToWorkshopItem(string itemUrl)
{
if (!isInitialized) return;
ulong id = GetWorkshopItemIDFromUrl(itemUrl);
SubscribeToWorkshopItem(id);
}
public static void SubscribeToWorkshopItem(ulong id)
public static void SubscribeToWorkshopItem(ulong id, Action onInstalled = null)
{
if (!isInitialized) return;
if (id == 0) { return; }
if (ugcSubscriptionTasks?.ContainsKey(id) ?? false) { return; }
if (pendingWorkshopSubscriptions.Contains(id)) { return; }
ugcSubscriptionTasks ??= new Dictionary<ulong, Task>();
ugcSubscriptionTasks.Add(id, Task.Run(async () =>
{
Steamworks.Ugc.Item? item = await Steamworks.SteamUGC.QueryFileAsync(id);
pendingWorkshopSubscriptions.Add(id);
TaskPool.Add(
$"SubscribeToWorkshopItem({id})",
Task.Run(async () =>
{
Steamworks.Ugc.Item? item = await Steamworks.SteamUGC.QueryFileAsync(id);
if (!item.HasValue)
{
DebugConsole.ThrowError("Failed to find a Steam Workshop item with the ID " + id.ToString() + ".");
return;
}
if (!item.HasValue)
{
DebugConsole.ThrowError($"Failed to find a Steam Workshop item with the ID {id}.");
return null;
}
bool subscribed = await item?.Subscribe();
if (!subscribed)
if (!(item?.IsSubscribed ?? false))
{
bool subscribed = await item?.Subscribe();
if (!subscribed)
{
DebugConsole.ThrowError($"Failed to subscribe to Steam Workshop item with the ID {id}.");
return null;
}
}
return item;
}),
(t) =>
{
DebugConsole.ThrowError("Failed to subscribe to Steam Workshop item with the ID " + id.ToString() + ".");
}
bool downloading = item?.Download() ?? false;
if (!downloading)
{
DebugConsole.ThrowError("Failed to start downloading Steam Workshop item with the ID " + id.ToString() + ".");
}
}));
bool shouldCleanup = true;
if (t.IsFaulted)
{
TaskPool.PrintTaskExceptions(t, $"Workshop subscription task {id} faulted");
}
else
{
var item = ((Task<Steamworks.Ugc.Item?>)t).Result;
if (item != null)
{
if (item?.IsInstalled ?? false)
{
onInstalled?.Invoke();
}
else
{
void _onInstalled()
{
onInstalled?.Invoke();
pendingWorkshopSubscriptions.Remove(id);
}
bool downloading = item?.Download(_onInstalled) ?? false;
if (!downloading)
{
DebugConsole.ThrowError($"Failed to start downloading Steam Workshop item with the ID {id}.");
}
else
{
shouldCleanup = false;
}
}
}
if (shouldCleanup)
{
pendingWorkshopSubscriptions.Remove(id);
}
}
});
}
public static void CreateWorkshopItemStaging(ContentPackage contentPackage, out Steamworks.Ugc.Editor? itemEditor)
@@ -790,9 +811,9 @@ namespace Barotrauma.Steam
itemEditor = itemEditor?.WithPrivateVisibility();
}
if (!CheckWorkshopItemEnabled(existingItem))
if (!CheckWorkshopItemInstalled(existingItem))
{
if (!EnableWorkShopItem(existingItem, out string errorMsg))
if (!InstallWorkshopItem(existingItem, out string errorMsg))
{
DebugConsole.NewMessage(errorMsg, Color.Red);
new GUIMessageBox(
@@ -804,9 +825,9 @@ namespace Barotrauma.Steam
}
}
ContentPackage tempContentPackage = new ContentPackage(Path.Combine(existingItem?.Directory, MetadataFileName)) { SteamWorkshopUrl = existingItem.Value.Url };
ContentPackage tempContentPackage = new ContentPackage(Path.Combine(existingItem?.Directory, MetadataFileName)) { SteamWorkshopId = existingItem.Value.Id };
string installedContentPackagePath = Path.GetFullPath(GetWorkshopItemContentPackagePath(tempContentPackage));
contentPackage = ContentPackage.List.Find(cp => Path.GetFullPath(cp.Path) == installedContentPackagePath);
contentPackage = ContentPackage.AllPackages.FirstOrDefault(cp => Path.GetFullPath(cp.Path) == installedContentPackagePath);
itemEditor = itemEditor?.WithContent(Path.GetDirectoryName(installedContentPackagePath));
@@ -923,7 +944,7 @@ namespace Barotrauma.Steam
workshopPublishStatus.Result = task.Result;
DebugConsole.NewMessage("Published workshop item " + item?.Title + " successfully.", Microsoft.Xna.Framework.Color.LightGreen);
contentPackage.SteamWorkshopUrl = $"http://steamcommunity.com/sharedfiles/filedetails/?source=Facepunch.Steamworks&id={task.Result.FileId.Value}";
contentPackage.SteamWorkshopId = task.Result.FileId.Value;
//NOTE: This sets InstallTime one hour into the future to guarantee
//that the published content package won't be autoupdated incorrectly.
//Change if it causes issues.
@@ -937,9 +958,9 @@ namespace Barotrauma.Steam
}
/// <summary>
/// Enables a workshop item by moving it to the game folder.
/// Installs a workshop item by moving it to the game folder.
/// </summary>
public static bool EnableWorkShopItem(Steamworks.Ugc.Item? item, out string errorMsg, bool selectContentPackage = false, bool suppressInstallNotif = false)
public static bool InstallWorkshopItem(Steamworks.Ugc.Item? item, out string errorMsg, bool enableContentPackage = false, bool suppressInstallNotif = false)
{
if (!(item?.IsInstalled ?? false))
{
@@ -959,11 +980,11 @@ namespace Barotrauma.Steam
ContentPackage contentPackage = new ContentPackage(metaDataFilePath)
{
SteamWorkshopUrl = item?.Url
SteamWorkshopId = item?.Id ?? 0
};
string newContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage);
List<ContentPackage> existingPackages = ContentPackage.List.Where(cp => cp.Path.CleanUpPath() == newContentPackagePath.CleanUpPath()).ToList();
List<ContentPackage> existingPackages = ContentPackage.AllPackages.Where(cp => cp.Path.CleanUpPath() == newContentPackagePath.CleanUpPath()).ToList();
if (existingPackages.Any())
{
if (item?.Owner.Id != Steamworks.SteamClient.SteamId)
@@ -975,7 +996,7 @@ namespace Barotrauma.Steam
}
else
{
RemoveMods(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && cp.SteamWorkshopUrl == contentPackage.SteamWorkshopUrl,
RemoveMods(cp => cp.SteamWorkshopId != 0 && cp.SteamWorkshopId == contentPackage.SteamWorkshopId,
false);
}
}
@@ -1024,7 +1045,7 @@ namespace Barotrauma.Steam
var newPackage = new ContentPackage(cp.Path, newContentPackagePath)
{
SteamWorkshopUrl = item?.Url,
SteamWorkshopId = item?.Id ?? 0,
InstallTime = item?.Updated > item?.Created ? item?.Updated : item?.Created
};
@@ -1045,17 +1066,17 @@ namespace Barotrauma.Steam
Directory.CreateDirectory(Path.GetDirectoryName(newContentPackagePath));
}
newPackage.Save(newContentPackagePath);
ContentPackage.List.Add(newPackage);
ContentPackage.AddPackage(newPackage);
if (selectContentPackage)
if (enableContentPackage)
{
if (newPackage.CorePackage)
if (newPackage.IsCorePackage)
{
GameMain.Config.SelectCorePackage(newPackage);
}
else
{
GameMain.Config.SelectContentPackage(newPackage);
GameMain.Config.EnableRegularPackage(newPackage);
}
GameMain.Config.SaveNewPlayerConfig();
@@ -1213,40 +1234,21 @@ namespace Barotrauma.Steam
return "";
}
private static bool CheckFileEquality(string filePath1, string filePath2)
{
if (filePath1 == filePath2)
{
return true;
}
using (FileStream fs1 = File.OpenRead(filePath1))
using (FileStream fs2 = File.OpenRead(filePath2))
{
Md5Hash hash1 = new Md5Hash(fs1);
Md5Hash hash2 = new Md5Hash(fs2);
return hash1.Hash == hash2.Hash;
}
}
private static void RemoveMods(Func<ContentPackage, bool> predicate, bool delete = true)
{
var toRemove = ContentPackage.List.Where(predicate).ToList();
var packagesToDeselect = GameMain.Config.SelectedContentPackages.Where(p => toRemove.Contains(p)).ToList();
var toRemoveCore = ContentPackage.CorePackages.Where(predicate).ToList();
if (toRemoveCore.Contains(GameMain.Config.CurrentCorePackage)) { GameMain.Config.AutoSelectCorePackage(toRemoveCore); }
var toRemoveRegular = ContentPackage.RegularPackages.Where(predicate).ToList();
var packagesToDeselect = GameMain.Config.EnabledRegularPackages.Where(p => toRemoveRegular.Contains(p)).ToList();
foreach (var cp in packagesToDeselect)
{
if (cp.CorePackage)
{
GameMain.Config.AutoSelectCorePackage(toRemove);
}
else
{
GameMain.Config.DeselectContentPackage(cp);
}
GameMain.Config.DisableRegularPackage(cp);
}
if (delete)
{
var toRemove = toRemoveCore.Concat(toRemoveRegular);
foreach (var cp in toRemove)
{
try
@@ -1258,22 +1260,19 @@ namespace Barotrauma.Steam
{
DebugConsole.ThrowError($"An error occurred while attempting to delete {Path.GetDirectoryName(cp.Path)}", e);
}
ContentPackage.RemovePackage(cp);
}
}
ContentPackage.List.RemoveAll(cp => toRemove.Contains(cp));
GameMain.Config.SelectedContentPackages.RemoveAll(cp => !ContentPackage.List.Contains(cp));
ContentPackage.SortContentPackages();
GameMain.Config.SaveNewPlayerConfig();
GameMain.Config.WarnIfContentPackageSelectionDirty();
}
/// <summary>
/// Disables a workshop item by removing the files from the game folder.
/// Uninstalls a workshop item by removing the files from the game folder.
/// </summary>
public static bool DisableWorkShopItem(Steamworks.Ugc.Item? item, bool noLog, out string errorMsg)
public static bool UninstallWorkshopItem(Steamworks.Ugc.Item? item, bool noLog, out string errorMsg)
{
errorMsg = null;
if (!(item?.IsInstalled ?? false))
@@ -1288,13 +1287,13 @@ namespace Barotrauma.Steam
ContentPackage contentPackage = new ContentPackage(Path.Combine(item?.Directory, MetadataFileName))
{
SteamWorkshopUrl = item?.Url
SteamWorkshopId = item?.Id ?? 0
};
GameMain.Config.SuppressModFolderWatcher = true;
try
{
RemoveMods(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && cp.SteamWorkshopUrl == contentPackage.SteamWorkshopUrl);
RemoveMods(cp => cp.SteamWorkshopId != 0 && cp.SteamWorkshopId == contentPackage.SteamWorkshopId);
}
catch (Exception e)
{
@@ -1330,7 +1329,7 @@ namespace Barotrauma.Steam
return contentPackage.IsCompatible();
}
public static bool CheckWorkshopItemEnabled(Steamworks.Ugc.Item? item)
public static bool CheckWorkshopItemInstalled(Steamworks.Ugc.Item? item)
{
if (!(item?.IsInstalled ?? false)) { return false; }
@@ -1359,7 +1358,7 @@ namespace Barotrauma.Steam
string errorMessage = "Metadata file for the Workshop item \"" + item?.Title +
"\" not found. Could not combine path (" + (item?.Directory ?? "directory name empty") + ").";
DebugConsole.ThrowError(errorMessage);
GameAnalyticsManager.AddErrorEventOnce("SteamManager.CheckWorkshopItemEnabled:PathCombineException" + item?.Title,
GameAnalyticsManager.AddErrorEventOnce("SteamManager.CheckWorkshopItemInstalled:PathCombineException" + item?.Title,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMessage);
return false;
@@ -1373,12 +1372,12 @@ namespace Barotrauma.Steam
ContentPackage contentPackage = new ContentPackage(metaDataPath)
{
SteamWorkshopUrl = item?.Url
SteamWorkshopId = item?.Id ?? 0
};
//make sure the contentpackage file is present
if (!File.Exists(GetWorkshopItemContentPackagePath(contentPackage)) ||
!ContentPackage.List.Any(cp => cp.SteamWorkshopUrl == contentPackage.SteamWorkshopUrl ||
(string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && cp.Name == contentPackage.Name)))
!ContentPackage.AllPackages.Any(cp => cp.SteamWorkshopId == contentPackage.SteamWorkshopId ||
(cp.SteamWorkshopId == 0 && cp.Name == contentPackage.Name)))
{
return false;
}
@@ -1399,9 +1398,9 @@ namespace Barotrauma.Steam
ContentPackage steamPackage = new ContentPackage(metaDataPath)
{
SteamWorkshopUrl = item?.Url
SteamWorkshopId = item?.Id ?? 0
};
ContentPackage myPackage = ContentPackage.List.Find(cp => cp.SteamWorkshopUrl == steamPackage.SteamWorkshopUrl);
ContentPackage myPackage = ContentPackage.AllPackages.FirstOrDefault(cp => cp.SteamWorkshopId == steamPackage.SteamWorkshopId);
if (myPackage?.InstallTime == null)
{
@@ -1427,7 +1426,7 @@ namespace Barotrauma.Steam
GameMain.Config.SuppressModFolderWatcher = true;
//remove mods that the player is no longer subscribed to
RemoveMods(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && !items.Any(it => it.Id == GetWorkshopItemIDFromUrl(cp.SteamWorkshopUrl)));
RemoveMods(cp => cp.SteamWorkshopId != 0 && !items.Any(it => it.Id == cp.SteamWorkshopId));
GameMain.Config.SuppressModFolderWatcher = false;
@@ -1441,9 +1440,9 @@ namespace Barotrauma.Steam
bool installedSuccessfully = false;
string errorMsg;
if (!CheckWorkshopItemEnabled(item))
if (!CheckWorkshopItemInstalled(item))
{
installedSuccessfully = EnableWorkShopItem(item, out errorMsg);
installedSuccessfully = InstallWorkshopItem(item, out errorMsg);
}
else if (!CheckWorkshopItemUpToDate(item))
{
@@ -1529,12 +1528,12 @@ namespace Barotrauma.Steam
{
errorMsg = "";
if (!(item?.IsInstalled ?? false)) { return false; }
bool reenable = GameMain.Config.SelectedContentPackages.Any(p => !string.IsNullOrEmpty(p.SteamWorkshopUrl) && GetWorkshopItemIDFromUrl(p.SteamWorkshopUrl) == item?.Id);
bool reenable = GameMain.Config.AllEnabledPackages.Any(p => p.SteamWorkshopId != 0 && p.SteamWorkshopId == item?.Id);
if (item?.Owner.Id != Steamworks.SteamClient.SteamId)
{
if (!DisableWorkShopItem(item, false, out errorMsg)) { return false; }
if (!UninstallWorkshopItem(item, false, out errorMsg)) { return false; }
}
if (!EnableWorkShopItem(item, errorMsg: out errorMsg, selectContentPackage: reenable)) { return false; }
if (!InstallWorkshopItem(item, errorMsg: out errorMsg, enableContentPackage: reenable)) { return false; }
return true;
}
@@ -1543,7 +1542,7 @@ namespace Barotrauma.Steam
string packageName = contentPackage.Name.Trim();
packageName = ToolBox.RemoveInvalidFileNameChars(packageName);
while (packageName.Last() == '.') { packageName = packageName.Substring(0, packageName.Length-1); }
//packageName = packageName + "_" + GetWorkshopItemIDFromUrl(contentPackage.SteamWorkshopUrl);
//packageName = packageName + "_" + contentPackage.SteamWorkshopId.ToString();
return Path.Combine("Mods", packageName, MetadataFileName);
}
@@ -1559,8 +1558,7 @@ namespace Barotrauma.Steam
attr.Name.ToString() == "characterfile") &&
attr.Value.CleanUpPath().Contains("/"))
{
ContentType type = ContentType.None;
Enum.TryParse(attr.Name.LocalName, true, out type);
Enum.TryParse(attr.Name.LocalName, true, out ContentType type);
attr.Value = CorrectContentFilePath(attr.Value, type, package, true);
}
}
@@ -1615,8 +1613,7 @@ namespace Barotrauma.Steam
if (checkIfFileExists)
{
bool exists = File.Exists(contentFilePath);
if (type == ContentType.Executable ||
type == ContentType.ServerExecutable)
if (type == ContentType.ServerExecutable)
{
exists |= File.Exists(Path.GetFileNameWithoutExtension(contentFilePath) + ".dll");
}
@@ -1634,7 +1631,7 @@ namespace Barotrauma.Steam
{
if (checkIfFileExists)
{
ContentPackage otherContentPackage = ContentPackage.List.Find(cp => cp.Name.Equals(splitPath[1], StringComparison.OrdinalIgnoreCase));
ContentPackage otherContentPackage = ContentPackage.AllPackages.FirstOrDefault(cp => cp.Name.Equals(splitPath[1], StringComparison.OrdinalIgnoreCase));
if (otherContentPackage != null)
{
string otherPackageName = Path.GetDirectoryName(otherContentPackage.Path);

View File

@@ -167,7 +167,7 @@ namespace Barotrauma.Networking
bool prevCaptured = true;
int captureTimer;
void UpdateCapture()
private void UpdateCapture()
{
Array.Copy(uncompressedBuffer, 0, prevUncompressedBuffer, 0, VoipConfig.BUFFER_SIZE);
Array.Clear(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE);
@@ -273,6 +273,11 @@ namespace Barotrauma.Networking
if (allowEnqueue || captureTimer > 0)
{
LastEnqueueAudio = DateTime.Now;
if (GameMain.Client?.Character != null)
{
var messageType = !ForceLocal && ChatMessage.CanUseRadio(GameMain.Client.Character, out _) ? ChatMessageType.Radio : ChatMessageType.Default;
GameMain.Client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
}
//encode audio and enqueue it
lock (buffers)
{

View File

@@ -91,13 +91,13 @@ namespace Barotrauma
if (userData == null) return;
foreach (GUIComponent comp in listBox.Content.Children)
{
if (comp.UserData != userData) continue;
GUITextBlock voteText = comp.FindChild("votes") as GUITextBlock;
if (voteText == null)
if (comp.UserData != userData) { continue; }
if (!(comp.FindChild("votes") is GUITextBlock voteText))
{
voteText = new GUITextBlock(new RectTransform(new Point(30, comp.Rect.Height), comp.RectTransform, Anchor.CenterRight),
"", textAlignment: Alignment.CenterRight)
{
Padding = Vector4.Zero,
UserData = "votes"
};
}

View File

@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Reflection.Metadata;
namespace Barotrauma.Particles
{
@@ -61,6 +62,8 @@ namespace Barotrauma.Particles
public bool HighQualityCollisionDetection;
public Vector4 ColorMultiplier;
public bool DrawOnTop { get; private set; }
public ParticlePrefab.DrawTargetType DrawTarget
@@ -141,7 +144,8 @@ namespace Barotrauma.Particles
color = prefab.StartColor;
changeColor = prefab.StartColor != prefab.EndColor;
ColorMultiplier = Vector4.One;
velocityChange = prefab.VelocityChangeDisplay;
velocityChangeWater = prefab.VelocityChangeWaterDisplay;
@@ -287,22 +291,42 @@ namespace Barotrauma.Particles
Vector2 collisionNormal = Vector2.Zero;
if (velocity.Y < 0.0f && position.Y - prefab.CollisionRadius * size.Y < hullRect.Y - hullRect.Height)
{
if (prefab.DeleteOnCollision) return false;
if (prefab.DeleteOnCollision) { return false; }
collisionNormal = new Vector2(0.0f, 1.0f);
}
else if (velocity.Y > 0.0f && position.Y + prefab.CollisionRadius * size.Y > hullRect.Y)
{
if (prefab.DeleteOnCollision) return false;
if (prefab.DeleteOnCollision) { return false; }
collisionNormal = new Vector2(0.0f, -1.0f);
}
else if (velocity.X < 0.0f && position.X - prefab.CollisionRadius * size.X < hullRect.X)
if (collisionNormal != Vector2.Zero)
{
if (prefab.DeleteOnCollision) return false;
bool gapFound = false;
foreach (Gap gap in hullGaps)
{
if (gap.Open <= 0.9f || gap.IsHorizontal) { continue; }
if (gap.WorldRect.X > position.X || gap.WorldRect.Right < position.X) { continue; }
float hullCenterY = currentHull.WorldRect.Y - currentHull.WorldRect.Height / 2;
int gapDir = Math.Sign(gap.WorldRect.Y - hullCenterY);
if (Math.Sign(velocity.Y) != gapDir || Math.Sign(position.Y - hullCenterY) != gapDir) { continue; }
gapFound = true;
break;
}
handleCollision(gapFound, collisionNormal);
}
if (velocity.X < 0.0f && position.X - prefab.CollisionRadius * size.X < hullRect.X)
{
if (prefab.DeleteOnCollision) { return false; }
collisionNormal = new Vector2(1.0f, 0.0f);
}
else if (velocity.X > 0.0f && position.X + prefab.CollisionRadius * size.X > hullRect.Right)
{
if (prefab.DeleteOnCollision) return false;
if (prefab.DeleteOnCollision) { return false; }
collisionNormal = new Vector2(-1.0f, 0.0f);
}
@@ -311,26 +335,21 @@ namespace Barotrauma.Particles
bool gapFound = false;
foreach (Gap gap in hullGaps)
{
if (gap.Open <= 0.9f || gap.IsHorizontal != (collisionNormal.X != 0.0f)) continue;
if (gap.Open <= 0.9f || !gap.IsHorizontal) { continue; }
if (gap.IsHorizontal)
{
if (gap.WorldRect.Y < position.Y || gap.WorldRect.Y - gap.WorldRect.Height > position.Y) continue;
int gapDir = Math.Sign(gap.WorldRect.Center.X - currentHull.WorldRect.Center.X);
if (Math.Sign(velocity.X) != gapDir || Math.Sign(position.X - currentHull.WorldRect.Center.X) != gapDir) continue;
}
else
{
if (gap.WorldRect.X > position.X || gap.WorldRect.Right < position.X) continue;
float hullCenterY = currentHull.WorldRect.Y - currentHull.WorldRect.Height / 2;
int gapDir = Math.Sign(gap.WorldRect.Y - hullCenterY);
if (Math.Sign(velocity.Y) != gapDir || Math.Sign(position.Y - hullCenterY) != gapDir) continue;
}
if (gap.WorldRect.Y < position.Y || gap.WorldRect.Y - gap.WorldRect.Height > position.Y) { continue; }
int gapDir = Math.Sign(gap.WorldRect.Center.X - currentHull.WorldRect.Center.X);
if (Math.Sign(velocity.X) != gapDir || Math.Sign(position.X - currentHull.WorldRect.Center.X) != gapDir) { continue; }
gapFound = true;
break;
}
handleCollision(gapFound, collisionNormal);
}
void handleCollision(bool gapFound, Vector2 collisionNormal)
{
if (!gapFound)
{
OnWallCollisionInside(currentHull, collisionNormal);
@@ -378,6 +397,8 @@ namespace Barotrauma.Particles
private void OnWallCollisionInside(Hull prevHull, Vector2 collisionNormal)
{
if (prevHull == null) { return; }
Rectangle prevHullRect = prevHull.WorldRect;
Vector2 subVel = prevHull?.Submarine != null ? ConvertUnits.ToDisplayUnits(prevHull.Submarine.Velocity) : Vector2.Zero;
@@ -465,12 +486,14 @@ namespace Barotrauma.Particles
drawSize *= ((totalLifeTime - lifeTime) / prefab.GrowTime);
}
Color currColor = new Color(color.ToVector4() * ColorMultiplier);
if (prefab.Sprites[spriteIndex] is SpriteSheet)
{
((SpriteSheet)prefab.Sprites[spriteIndex]).Draw(
spriteBatch, animFrame,
new Vector2(drawPosition.X, -drawPosition.Y),
color * (color.A / 255.0f),
currColor * (currColor.A / 255.0f),
prefab.Sprites[spriteIndex].Origin, drawRotation,
drawSize, SpriteEffects.None, prefab.Sprites[spriteIndex].Depth);
}
@@ -478,7 +501,7 @@ namespace Barotrauma.Particles
{
prefab.Sprites[spriteIndex].Draw(spriteBatch,
new Vector2(drawPosition.X, -drawPosition.Y),
color * (color.A / 255.0f),
currColor * (currColor.A / 255.0f),
prefab.Sprites[spriteIndex].Origin, drawRotation,
drawSize, SpriteEffects.None, prefab.Sprites[spriteIndex].Depth);
}

View File

@@ -23,7 +23,7 @@ namespace Barotrauma.Particles
Prefab = prefab;
}
public void Emit(float deltaTime, Vector2 position, Hull hullGuess = null, float angle = 0.0f, float particleRotation = 0.0f, float velocityMultiplier = 1.0f, float sizeMultiplier = 1.0f, float amountMultiplier = 1.0f)
public void Emit(float deltaTime, Vector2 position, Hull hullGuess = null, float angle = 0.0f, float particleRotation = 0.0f, float velocityMultiplier = 1.0f, float sizeMultiplier = 1.0f, float amountMultiplier = 1.0f, Color? colorMultiplier = null)
{
emitTimer += deltaTime * amountMultiplier;
burstEmitTimer -= deltaTime;
@@ -33,7 +33,7 @@ namespace Barotrauma.Particles
float emitInterval = 1.0f / Prefab.ParticlesPerSecond;
while (emitTimer > emitInterval)
{
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier);
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier);
emitTimer -= emitInterval;
}
}
@@ -43,11 +43,11 @@ namespace Barotrauma.Particles
burstEmitTimer = Prefab.EmitInterval;
for (int i = 0; i < Prefab.ParticleAmount * amountMultiplier; i++)
{
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier);
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier);
}
}
private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier)
private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null)
{
angle += Rand.Range(Prefab.AngleMin, Prefab.AngleMax);
@@ -61,6 +61,7 @@ namespace Barotrauma.Particles
{
particle.Size *= Rand.Range(Prefab.ScaleMin, Prefab.ScaleMax) * sizeMultiplier;
particle.HighQualityCollisionDetection = Prefab.HighQualityCollisionDetection;
if (colorMultiplier.HasValue) { particle.ColorMultiplier = colorMultiplier.Value.ToVector4(); }
}
}

View File

@@ -157,9 +157,9 @@ namespace Barotrauma
sb.AppendLine("VSync " + (GameMain.Config.VSyncEnabled ? "ON" : "OFF"));
sb.AppendLine("Language: " + (GameMain.Config.Language ?? "none"));
}
if (GameMain.SelectedPackages != null)
if (GameMain.Config.AllEnabledPackages != null)
{
sb.AppendLine("Selected content packages: " + (!GameMain.SelectedPackages.Any() ? "None" : string.Join(", ", GameMain.SelectedPackages.Select(c => c.Name))));
sb.AppendLine("Selected content packages: " + (!GameMain.Config.AllEnabledPackages.Any() ? "None" : string.Join(", ", GameMain.Config.AllEnabledPackages.Select(c => c.Name))));
}
sb.AppendLine("Level seed: " + ((Level.Loaded == null) ? "no level loaded" : Level.Loaded.Seed));
sb.AppendLine("Loaded submarine: " + ((Submarine.MainSub == null) ? "None" : Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash + ")"));

View File

@@ -26,6 +26,9 @@ namespace Barotrauma
public Action<SubmarineInfo, string, string> StartNewGame;
public Action<string> LoadGame;
private enum CategoryFilter { All = 0, Vanilla = 1, Custom = 2 };
private CategoryFilter subFilter = CategoryFilter.All;
public GUIButton StartButton
{
get;
@@ -74,6 +77,12 @@ namespace Barotrauma
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("SelectedSub"), font: GUI.SubHeadingFont);
var moddedDropdown = new GUIDropDown(new RectTransform(new Vector2(1f, 0.02f), leftColumn.RectTransform), "", 3);
moddedDropdown.AddItem(TextManager.Get("clientpermission.all"), CategoryFilter.All);
moddedDropdown.AddItem(TextManager.Get("servertag.modded.false"), CategoryFilter.Vanilla);
moddedDropdown.AddItem(TextManager.Get("customrank"), CategoryFilter.Custom);
moddedDropdown.Select(0);
var filterContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), isHorizontal: true)
{
Stretch = true
@@ -88,6 +97,14 @@ namespace Barotrauma
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };
searchBox.OnTextChanged += (textBox, text) => { FilterSubs(subList, text); return true; };
moddedDropdown.OnSelected = (component, data) =>
{
searchBox.Text = string.Empty;
subFilter = (CategoryFilter)data;
UpdateSubList(SubmarineInfo.SavedSubmarines);
return true;
};
subList.OnSelected = OnSubSelected;
}
else // Spacing to fix the multiplayer campaign setup layout
@@ -234,7 +251,7 @@ namespace Barotrauma
Stretch = true
};
var subLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.055f), subHolder.RectTransform) { MinSize = new Point(0, 25) }, TextManager.Language == "English" ? "Purchasable submarines" : TextManager.Get("workshoplabelsubmarines"), font: GUI.SubHeadingFont);
var subLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.055f), subHolder.RectTransform) { MinSize = new Point(0, 25) }, TextManager.Get("purchasablesubmarines", fallBackTag: "workshoplabelsubmarines"), font: GUI.SubHeadingFont);
var filterContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subHolder.RectTransform), isHorizontal: true)
{
@@ -395,7 +412,16 @@ namespace Barotrauma
public void UpdateSubList(IEnumerable<SubmarineInfo> submarines)
{
var subsToShow = submarines.Where(s => s.IsCampaignCompatibleIgnoreClass).ToList();
List<SubmarineInfo> subsToShow;
if (!isMultiplayer && subFilter != CategoryFilter.All)
{
subsToShow = submarines.Where(s => s.IsCampaignCompatibleIgnoreClass && s.IsVanillaSubmarine() == (subFilter == CategoryFilter.Vanilla)).ToList();
}
else
{
subsToShow = submarines.Where(s => s.IsCampaignCompatibleIgnoreClass).ToList();
}
subsToShow.Sort((s1, s2) =>
{
int p1 = s1.Price > CampaignMode.MaxInitialSubmarinePrice ? 10 : 0;
@@ -428,18 +454,22 @@ namespace Barotrauma
ToolTip = textBlock.ToolTip
};
#if !DEBUG
if (sub.Price > CampaignMode.MaxInitialSubmarinePrice && !GameMain.DebugDraw)
if (!GameMain.DebugDraw)
{
textBlock.CanBeFocused = false;
if (sub.Price > CampaignMode.MaxInitialSubmarinePrice || !sub.IsCampaignCompatible)
{
textBlock.CanBeFocused = false;
textBlock.TextColor *= 0.5f;
}
}
#endif
}
if (SubmarineInfo.SavedSubmarines.Any())
{
var nonShuttles = subsToShow.Where(s => s.Type == SubmarineType.Player && !s.HasTag(SubmarineTag.Shuttle) && s.Price <= CampaignMode.MaxInitialSubmarinePrice).ToList();
if (nonShuttles.Count > 0)
var validSubs = subsToShow.Where(s => s.IsCampaignCompatible && s.Price <= CampaignMode.MaxInitialSubmarinePrice).ToList();
if (validSubs.Count > 0)
{
subList.Select(nonShuttles[Rand.Int(nonShuttles.Count)]);
subList.Select(validSubs[Rand.Int(validSubs.Count)]);
}
}
}
@@ -448,6 +478,7 @@ namespace Barotrauma
public void UpdateLoadMenu(IEnumerable<string> saveFiles = null)
{
prevSaveFiles?.Clear();
prevSaveFiles = null;
loadGameContainer.ClearChildren();
if (saveFiles == null)
@@ -504,6 +535,7 @@ namespace Barotrauma
};
bool isCompatible = true;
prevSaveFiles ??= new List<string>();
if (!isMultiplayer)
{
nameText.Text = Path.GetFileNameWithoutExtension(saveFile);
@@ -529,10 +561,10 @@ namespace Barotrauma
}
else
{
prevSaveFiles?.Add(saveFile);
string[] splitSaveFile = saveFile.Split(';');
saveFrame.UserData = splitSaveFile[0];
fileName = nameText.Text = Path.GetFileNameWithoutExtension(splitSaveFile[0]);
prevSaveFiles?.Add(fileName);
if (splitSaveFile.Length > 1) { subName = splitSaveFile[1]; }
if (splitSaveFile.Length > 2) { saveTime = splitSaveFile[2]; }
if (splitSaveFile.Length > 3) { contentPackageStr = splitSaveFile[3]; }
@@ -545,7 +577,7 @@ namespace Barotrauma
if (!string.IsNullOrEmpty(contentPackageStr))
{
List<string> contentPackagePaths = contentPackageStr.Split('|').ToList();
if (!GameSession.IsCompatibleWithSelectedContentPackages(contentPackagePaths, out string errorMsg))
if (!GameSession.IsCompatibleWithEnabledContentPackages(contentPackagePaths, out string errorMsg))
{
nameText.TextColor = GUI.Style.Red;
saveFrame.ToolTip = string.Join("\n", errorMsg, TextManager.Get("campaignmode.contentpackagemismatchwarning"));
@@ -696,9 +728,16 @@ namespace Barotrauma
string saveFile = obj as string;
if (obj == null) { return false; }
SaveUtil.DeleteSave(saveFile);
prevSaveFiles?.Remove(saveFile);
UpdateLoadMenu(prevSaveFiles);
string header = TextManager.Get("deletedialoglabel");
string body = TextManager.GetWithVariable("deletedialogquestion", "[file]", Path.GetFileNameWithoutExtension(saveFile));
EventEditorScreen.AskForConfirmation(header, body, () =>
{
SaveUtil.DeleteSave(saveFile);
prevSaveFiles?.RemoveAll(s => s.StartsWith(saveFile));
UpdateLoadMenu(prevSaveFiles.ToList());
return true;
});
return true;
}

View File

@@ -497,7 +497,7 @@ namespace Barotrauma
};
if (Level.Loaded != null &&
connection.LevelData == Level.Loaded.LevelData &&
connection?.LevelData == Level.Loaded.LevelData &&
currentDisplayLocation == Campaign.Map?.CurrentLocation)
{
StartButton.Visible = false;

View File

@@ -1655,9 +1655,9 @@ namespace Barotrauma.CharacterEditor
if (contentPackage == null)
{
#if DEBUG
contentPackage = GameMain.Config.SelectedContentPackages.LastOrDefault();
contentPackage = GameMain.Config.AllEnabledPackages.LastOrDefault();
#else
contentPackage = GameMain.Config.SelectedContentPackages.LastOrDefault(cp => cp != vanilla);
contentPackage = GameMain.Config.AllEnabledPackages.LastOrDefault(cp => cp != vanilla);
#endif
}
if (contentPackage == null)
@@ -1674,9 +1674,9 @@ namespace Barotrauma.CharacterEditor
}
#endif
// Content package
if (!GameMain.Config.SelectedContentPackages.Contains(contentPackage))
if (!GameMain.Config.AllEnabledPackages.Contains(contentPackage))
{
GameMain.Config.SelectContentPackage(contentPackage);
GameMain.Config.EnableRegularPackage(contentPackage);
}
GameMain.Config.SaveNewPlayerConfig();
@@ -1757,9 +1757,10 @@ namespace Barotrauma.CharacterEditor
#endif
// Add to the selected content package
contentPackage.AddFile(configFilePath, ContentType.Character);
Barotrauma.IO.Validation.DevException = true;
contentPackage.Save(contentPackage.Path);
DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path));
CharacterPrefab.LoadFromFile(configFilePath, contentPackage, forceOverride: true);
Barotrauma.IO.Validation.DevException = false;
DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path));
// Ragdoll
RagdollParams.ClearCache();
@@ -1783,7 +1784,11 @@ namespace Barotrauma.CharacterEditor
element.SetAttributeValue("type", name);
string fullPath = AnimationParams.GetDefaultFile(name, animation.AnimationType, contentPackage);
element.Name = AnimationParams.GetDefaultFileName(name, animation.AnimationType);
#if DEBUG
element.Save(fullPath);
#else
element.SaveSafe(fullPath);
#endif
}
}
else

View File

@@ -305,7 +305,7 @@ namespace Barotrauma.CharacterEditor
new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), mainElement.RectTransform, Anchor.CenterLeft), TextManager.Get("ContentPackage"));
var rightContainer = new GUIFrame(new RectTransform(new Vector2(0.7f, 1), mainElement.RectTransform, Anchor.CenterRight), style: null);
contentPackageDropDown = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.5f), rightContainer.RectTransform, Anchor.TopRight));
foreach (ContentPackage cp in ContentPackage.List)
foreach (ContentPackage cp in ContentPackage.AllPackages)
{
#if !DEBUG
if (cp == GameMain.VanillaContent) { continue; }
@@ -334,15 +334,15 @@ namespace Barotrauma.CharacterEditor
contentPackageNameElement.Flash();
return false;
}
if (ContentPackage.List.Any(cp => cp.Name.ToLower() == contentPackageNameElement.Text.ToLower()))
if (ContentPackage.AllPackages.Any(cp => cp.Name.ToLower() == contentPackageNameElement.Text.ToLower()))
{
new GUIMessageBox("", TextManager.Get("charactereditor.contentpackagenameinuse", fallBackTag: "leveleditorlevelobjnametaken"));
return false;
}
string modName = ToolBox.RemoveInvalidFileNameChars(contentPackageNameElement.Text);
ContentPackage = ContentPackage.CreatePackage(contentPackageNameElement.Text, Path.Combine("Mods", modName, Steam.SteamManager.MetadataFileName), false);
ContentPackage.List.Add(ContentPackage);
GameMain.Config.SelectContentPackage(ContentPackage);
ContentPackage.AddPackage(ContentPackage);
GameMain.Config.EnableRegularPackage(ContentPackage);
contentPackageDropDown.AddItem(ContentPackage.Name, ContentPackage, ContentPackage.Path);
contentPackageDropDown.SelectItem(ContentPackage);
contentPackageNameElement.Text = "";

View File

@@ -32,10 +32,13 @@ namespace Barotrauma
private EditorNode? draggedNode;
private Vector2 dragOffset;
private Dictionary<EditorNode, Vector2> markedNodes = new Dictionary<EditorNode, Vector2>();
private readonly Dictionary<EditorNode, Vector2> markedNodes = new Dictionary<EditorNode, Vector2>();
private static string projectName = string.Empty;
private OutpostGenerationParams? lastTestParam;
private LocationType? lastTestType;
private int CreateID()
{
int maxId = nodeList.Any() ? nodeList.Max(node => node.ID) : 0;
@@ -292,6 +295,8 @@ namespace Barotrauma
string filePath = System.IO.Path.Combine(directory, $"{projectName}.sevproj");
File.WriteAllText(Path.Combine(directory, $"{projectName}.sevproj"), save.ToString());
GUI.AddMessage($"Project saved to {filePath}", GUI.Style.Green);
AskForConfirmation(TextManager.Get("EventEditor.TestPromptHeader"), TextManager.Get("EventEditor.TestPromptBody"), CreateTestSetupMenu);
return true;
};
return true;
@@ -520,15 +525,12 @@ namespace Barotrauma
public override void Select()
{
Cam.Position = Vector2.Zero;
nodeList.Clear();
projectName = TextManager.Get("EventEditor.Unnamed");
base.Select();
}
public override void Deselect()
{
nodeList.Clear();
base.Deselect();
}
@@ -597,9 +599,9 @@ namespace Barotrauma
optionElement.Add(new XAttribute("text", text));
if (end) { optionElement.Add(new XAttribute("endconversation", true)); }
if (node != null)
if (node is EventNode eventNode)
{
ExportChildNodes((EventNode) node, optionElement);
ExportChildNodes(eventNode, optionElement);
}
newElement.Add(optionElement);
@@ -748,6 +750,57 @@ namespace Barotrauma
return true;
};
}
private bool CreateTestSetupMenu()
{
var msgBox = new GUIMessageBox(TextManager.Get("EventEditor.TestPromptHeader"), "", new[] { TextManager.Get("Cancel"), TextManager.Get("OK") },
relativeSize: new Vector2(0.2f, 0.3f), minSize: new Point(300, 175));
var layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), msgBox.Content.RectTransform));
new GUITextBlock(new RectTransform(new Vector2(1, 0.25f), layout.RectTransform), TextManager.Get("EventEditor.OutpostGenParams"), font: GUI.SubHeadingFont);
GUIDropDown paramInput = new GUIDropDown(new RectTransform(new Vector2(1, 0.25f), layout.RectTransform), string.Empty, OutpostGenerationParams.Params.Count);
foreach (OutpostGenerationParams param in OutpostGenerationParams.Params)
{
paramInput.AddItem(param.Identifier, param);
}
paramInput.OnSelected = (_, param) =>
{
lastTestParam = param as OutpostGenerationParams;
return true;
};
paramInput.SelectItem(lastTestParam ?? OutpostGenerationParams.Params.FirstOrDefault());
new GUITextBlock(new RectTransform(new Vector2(1, 0.25f), layout.RectTransform), TextManager.Get("EventEditor.LocationType"), font: GUI.SubHeadingFont);
GUIDropDown typeInput = new GUIDropDown(new RectTransform(new Vector2(1, 0.25f), layout.RectTransform), string.Empty, LocationType.List.Count);
foreach (LocationType type in LocationType.List)
{
typeInput.AddItem(type.Identifier, type);
}
typeInput.OnSelected = (_, type) =>
{
lastTestType = type as LocationType;
return true;
};
typeInput.SelectItem(lastTestType ?? LocationType.List.FirstOrDefault());
// Cancel button
msgBox.Buttons[0].OnClicked = (button, o) =>
{
msgBox.Close();
return true;
};
// Ok button
msgBox.Buttons[1].OnClicked = (button, o) =>
{
TestEvent(lastTestParam, lastTestType);
msgBox.Close();
return true;
};
return true;
}
private static void CreateEditMenu(ValueNode? node, NodeConnection? connection = null)
{
@@ -860,6 +913,40 @@ namespace Barotrauma
};
}
private bool TestEvent(OutpostGenerationParams? param, LocationType? type)
{
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(info => info.HasTag(SubmarineTag.Shuttle));
XElement? eventXml = ExportXML();
EventPrefab? prefab;
if (eventXml != null)
{
prefab = new EventPrefab(eventXml);
}
else
{
GUI.AddMessage("Unable to open test enviroment because the event contains errors.", GUI.Style.Red);
return false;
}
GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, null);
TestGameMode gameMode = (TestGameMode) gameSession.GameMode;
gameMode.SpawnOutpost = true;
gameMode.OutpostParams = param;
gameMode.OutpostType = type;
gameMode.TriggeredEvent = prefab;
gameMode.OnRoundEnd = () =>
{
Submarine.Unload();
GameMain.EventEditorScreen.Select();
};
GameMain.GameScreen.Select();
gameSession.StartRound(null, false);
return true;
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
DrawnTooltip = string.Empty;

View File

@@ -18,7 +18,7 @@ namespace Barotrauma
private Effect damageEffect;
private Texture2D damageStencil;
private Texture2D distortTexture;
private Texture2D distortTexture;
private float fadeToBlackState;
@@ -171,6 +171,7 @@ namespace Barotrauma
//These will be visible through the LOS effect.
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
Submarine.DrawBack(spriteBatch, false, e => e is Structure s && (e.SpriteDepth >= 0.9f || s.Prefab.BackgroundSprite != null));
Submarine.DrawPaintedColors(spriteBatch, false);
spriteBatch.End();
graphics.SetRenderTarget(null);

View File

@@ -274,9 +274,9 @@ namespace Barotrauma
}
//TODO: hacky workaround to check for wrecks and outposts, refactor SubmarineInfo and ContentType at some point
var nonPlayerFiles = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.Wreck).ToList();
nonPlayerFiles.AddRange(ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.Outpost));
nonPlayerFiles.AddRange(ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.OutpostModule));
var nonPlayerFiles = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.Wreck).ToList();
nonPlayerFiles.AddRange(ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.Outpost));
nonPlayerFiles.AddRange(ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.OutpostModule));
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name.Equals(GameMain.Config.QuickStartSubmarineName, StringComparison.InvariantCultureIgnoreCase));
subInfo ??= SubmarineInfo.SavedSubmarines.GetRandom(s =>
s.IsPlayer && !s.HasTag(SubmarineTag.Shuttle) &&

View File

@@ -41,7 +41,6 @@ namespace Barotrauma
private Tab selectedTab;
private Sprite backgroundSprite;
private Sprite backgroundVignette;
private readonly GUIComponent titleText;
@@ -65,8 +64,6 @@ namespace Barotrauma
CreateCampaignSetupUI();
};
backgroundVignette = new Sprite("Content/UI/MainMenuVignette.png", Vector2.Zero);
new GUIImage(new RectTransform(new Vector2(0.4f, 0.25f), Frame.RectTransform, Anchor.BottomRight)
{ RelativeOffset = new Vector2(0.08f, 0.05f), AbsoluteOffset = new Point(-8, -8) },
style: "TitleText")
@@ -863,7 +860,7 @@ namespace Barotrauma
GameMain.NetLobbyScreen = new NetLobbyScreen();
try
{
string exeName = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.ServerExecutable)?.FirstOrDefault()?.Path;
string exeName = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.ServerExecutable)?.FirstOrDefault()?.Path;
if (string.IsNullOrEmpty(exeName))
{
DebugConsole.ThrowError("No server executable defined in the selected content packages. Attempting to use the default executable...");
@@ -947,14 +944,11 @@ namespace Barotrauma
#if USE_STEAM
if (GameMain.Config.UseSteamMatchmaking)
{
joinServerButton.Enabled = Steam.SteamManager.IsInitialized;
hostServerButton.Enabled = Steam.SteamManager.IsInitialized;
hostServerButton.Enabled = Steam.SteamManager.IsInitialized;
}
steamWorkshopButton.Enabled = Steam.SteamManager.IsInitialized;
steamWorkshopButton.Enabled = Steam.SteamManager.IsInitialized;
#endif
#else
joinServerButton.Enabled = true;
hostServerButton.Enabled = true;
#if USE_STEAM
steamWorkshopButton.Enabled = true;
#endif
@@ -976,10 +970,14 @@ namespace Barotrauma
aberrationStrength: 0.0f);
}
spriteBatch.Begin(blendState: BlendState.NonPremultiplied);
backgroundVignette.Draw(spriteBatch, Vector2.Zero, Color.White, Vector2.Zero, 0.0f,
new Vector2(GameMain.GraphicsWidth / backgroundVignette.size.X, GameMain.GraphicsHeight / backgroundVignette.size.Y));
spriteBatch.End();
var vignette = GUI.Style.GetComponentStyle("mainmenuvignette")?.GetDefaultSprite();
if (vignette != null)
{
spriteBatch.Begin(blendState: BlendState.NonPremultiplied);
vignette.Draw(spriteBatch, Vector2.Zero, Color.White, Vector2.Zero, 0.0f,
new Vector2(GameMain.GraphicsWidth / vignette.size.X, GameMain.GraphicsHeight / vignette.size.Y));
spriteBatch.End();
}
}
readonly string[] legalCrap = new string[]

View File

@@ -15,20 +15,20 @@ namespace Barotrauma
private readonly List<Sprite> characterSprites = new List<Sprite>();
//private readonly List<Sprite> jobPreferenceSprites = new List<Sprite>();
private GUIFrame infoFrame, modeFrame;
private GUILayoutGroup infoFrameContent;
private GUIFrame myCharacterFrame;
private readonly GUIFrame infoFrame, modeFrame;
private readonly GUILayoutGroup infoFrameContent;
private readonly GUIFrame myCharacterFrame;
private GUIListBox subList, modeList;
private readonly GUIListBox subList, modeList;
private GUIListBox chatBox, playerList;
private GUIButton serverLogReverseButton;
private GUIListBox serverLogBox, serverLogFilterTicks;
private readonly GUIListBox chatBox, playerList;
private readonly GUIButton serverLogReverseButton;
private readonly GUIListBox serverLogBox, serverLogFilterTicks;
private GUIComponent jobVariantTooltip;
private GUITextBox chatInput;
private GUITextBox serverLogFilter;
private readonly GUITextBox chatInput;
private readonly GUITextBox serverLogFilter;
public GUITextBox ChatInput
{
get
@@ -82,10 +82,10 @@ namespace Barotrauma
private readonly GUITickBox autoRestartBox;
private readonly GUITextBlock autoRestartText;
private GUIDropDown shuttleList;
private GUITickBox shuttleTickBox;
private readonly GUIDropDown shuttleList;
private readonly GUITickBox shuttleTickBox;
private GUIComponent settingsBlocker;
private readonly GUIComponent settingsBlocker;
private Sprite backgroundSprite;
@@ -123,15 +123,6 @@ namespace Barotrauma
public GUIProgressBar FileTransferProgressBar { get; private set; }
public GUITextBlock FileTransferProgressText { get; private set; }
private bool AllowSubSelection
{
get
{
return GameMain.NetworkMember.ServerSettings.Voting.AllowSubVoting ||
(GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.SelectSub));
}
}
public GUITextBox ServerName
{
get;
@@ -150,8 +141,8 @@ namespace Barotrauma
private set;
}
private GUIButton showChatButton;
private GUIButton showLogButton;
private readonly GUIButton showChatButton;
private readonly GUIButton showLogButton;
public GUIListBox SubList
{
@@ -268,9 +259,7 @@ namespace Barotrauma
foreach (MissionType type in Enum.GetValues(typeof(MissionType)))
{
if (type == MissionType.None || type == MissionType.All) { continue; }
missionTypeTickBoxes[index].Selected = (((int)type & (int)value) != 0);
missionTypeTickBoxes[index].Selected = ((int)type & (int)value) != 0;
index++;
}
}
@@ -290,8 +279,7 @@ namespace Barotrauma
List<Pair<JobPrefab, int>> jobPreferences = new List<Pair<JobPrefab, int>>();
foreach (GUIComponent child in JobList.Content.Children)
{
var jobPrefab = child.UserData as Pair<JobPrefab, int>;
if (jobPrefab == null) { continue; }
if (!(child.UserData is Pair<JobPrefab, int> jobPrefab)) { continue; }
jobPreferences.Add(jobPrefab);
}
return jobPreferences;
@@ -743,7 +731,7 @@ namespace Barotrauma
foreach (GUIComponent child in subList.Content.Children)
{
if (!(child.UserData is SubmarineInfo sub)) { continue; }
child.Visible = string.IsNullOrEmpty(text) ? true : sub.DisplayName.ToLower().Contains(text.ToLower());
child.Visible = string.IsNullOrEmpty(text) || sub.DisplayName.ToLower().Contains(text.ToLower());
}
return true;
};
@@ -887,7 +875,12 @@ namespace Barotrauma
ContinueCampaignButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.3f), campaignContent.RectTransform),
TextManager.Get("campaigncontinue"), textAlignment: Alignment.Center)
{
OnClicked = (_, __) => { GameMain.Client?.RequestStartRound(true); return true; }
OnClicked = (_, __) =>
{
CoroutineManager.StartCoroutine(WaitForStartRound(ContinueCampaignButton), "WaitForStartRound");
GameMain.Client?.RequestStartRound(true);
return true;
}
};
QuitCampaignButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.3f), campaignContent.RectTransform),
TextManager.Get("pausemenusavequit"), textAlignment: Alignment.Center)
@@ -1356,6 +1349,7 @@ namespace Barotrauma
if (GameMain.Client == null) { return; }
string newName = Client.SanitizeName(tb.Text);
newName = newName.Replace(":", "").Replace(";", "");
if (newName == GameMain.Client.Name) return;
if (string.IsNullOrWhiteSpace(newName))
{
tb.Text = GameMain.Client.Name;
@@ -1365,6 +1359,8 @@ namespace Barotrauma
if (isGameRunning)
{
GameMain.Client.PendingName = tb.Text;
TabMenu.PendingChanges = true;
CreateChangesPendingText();
}
else
{
@@ -1603,13 +1599,13 @@ namespace Barotrauma
private void AddSubmarine(GUIComponent subList, SubmarineInfo sub)
{
if (subList is GUIListBox)
if (subList is GUIListBox listBox)
{
subList = ((GUIListBox)subList).Content;
subList = listBox.Content;
}
else if (subList is GUIDropDown)
else if (subList is GUIDropDown dropDown)
{
subList = ((GUIDropDown)subList).ListBox.Content;
subList = dropDown.ListBox.Content;
}
var frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), subList.RectTransform) { MinSize = new Point(0, 20) },
@@ -1655,7 +1651,7 @@ namespace Barotrauma
if (sub.HasTag(SubmarineTag.Shuttle))
{
var shuttleText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), frame.RectTransform, Anchor.CenterRight),
var shuttleText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), frame.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(GUI.IntScale(20), 0) },
TextManager.Get("Shuttle", fallBackTag: "RespawnShuttle"), textAlignment: Alignment.CenterRight, font: GUI.SmallFont)
{
TextColor = subTextBlock.TextColor * 0.8f,
@@ -1665,16 +1661,16 @@ namespace Barotrauma
//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)
{
subTextBlock.TextColor *= 0.5f;
subTextBlock.TextColor *= 0.8f;
foreach (GUIComponent child in frame.Children)
{
child.Color *= 0.5f;
child.Color *= 0.8f;
}
}
}
else
{
var classText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), frame.RectTransform, Anchor.CenterRight),
var classText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), frame.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(GUI.IntScale(20), 0) },
TextManager.Get($"submarineclass.{sub.SubmarineClass}"), textAlignment: Alignment.CenterRight, font: GUI.SmallFont)
{
UserData = "classtext",
@@ -1816,20 +1812,20 @@ namespace Barotrauma
public void SetPlayerNameAndJobPreference(Client client)
{
var PlayerFrame = (GUITextBlock)PlayerList.Content.FindChild(client);
if (PlayerFrame == null) { return; }
PlayerFrame.Text = client.Name;
var playerFrame = (GUITextBlock)PlayerList.Content.FindChild(client);
if (playerFrame == null) { return; }
playerFrame.Text = client.Name;
Color color = Color.White;
if (JobPrefab.Prefabs.ContainsKey(client.PreferredJob))
{
color = JobPrefab.Prefabs[client.PreferredJob].UIColor;
}
PlayerFrame.Color = color * 0.4f;
PlayerFrame.HoverColor = color * 0.6f;
PlayerFrame.SelectedColor = color * 0.8f;
PlayerFrame.OutlineColor = color * 0.5f;
PlayerFrame.TextColor = color;
playerFrame.Color = color * 0.4f;
playerFrame.HoverColor = color * 0.6f;
playerFrame.SelectedColor = color * 0.8f;
playerFrame.OutlineColor = color * 0.5f;
playerFrame.TextColor = color;
}
public void SetPlayerVoiceIconState(Client client, bool muted, bool mutedLocally)
@@ -2671,7 +2667,7 @@ namespace Barotrauma
return false;
}
private bool SwitchJob(GUIButton button, object obj)
private bool SwitchJob(GUIButton _, object obj)
{
if (JobList == null) { return false; }
@@ -2724,7 +2720,7 @@ namespace Barotrauma
return false;
}
private bool OpenJobSelection(GUIComponent child, object userData)
private bool OpenJobSelection(GUIComponent _, object __)
{
if (JobSelectionFrame != null)
{
@@ -2870,7 +2866,9 @@ namespace Barotrauma
{
Color = Color.Black,
HoverColor = Color.Black,
SelectedColor = Color.Black
PressedColor = Color.Black,
SelectedColor = Color.Black,
CanBeFocused = false
};
var textBlock = new GUITextBlock(
@@ -2883,6 +2881,7 @@ namespace Barotrauma
HoverColor = Color.Transparent,
SelectedColor = Color.Transparent,
TextColor = jobPrefab.UIColor,
HoverTextColor = Color.Lerp(jobPrefab.UIColor, Color.White, 0.5f),
CanBeFocused = false,
AutoScaleHorizontal = true
};
@@ -2938,7 +2937,7 @@ namespace Barotrauma
info.Head = new CharacterInfo.HeadInfo(info.HeadSpriteId, info.Gender, info.Race, info.HairIndex, info.BeardIndex, index, info.FaceAttachmentIndex);
break;
default:
DebugConsole.ThrowError($"Wearable type not implemented: {type.ToString()}");
DebugConsole.ThrowError($"Wearable type not implemented: {type}");
return false;
}
info.ReloadHeadAttachments();

View File

@@ -13,6 +13,8 @@ namespace Barotrauma
private RectTransform prevGuiElementParent;
public Exception LoadException;
public static RoundSummaryScreen Select(Sprite backgroundSprite, RoundSummary roundSummary)
{
var summaryScreen = new RoundSummaryScreen()
@@ -51,5 +53,16 @@ namespace Barotrauma
spriteBatch.End();
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
if (LoadException != null)
{
var temp = LoadException;
LoadException = null;
throw temp;
}
}
}
}

View File

@@ -1,4 +1,5 @@
using Barotrauma.Extensions;
using Barotrauma.IO;
using Barotrauma.Networking;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
@@ -6,13 +7,10 @@ using Microsoft.Xna.Framework.Graphics;
using RestSharp;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
@@ -21,7 +19,7 @@ namespace Barotrauma
class ServerListScreen : Screen
{
//how often the client is allowed to refresh servers
private TimeSpan AllowedRefreshInterval = new TimeSpan(0, 0, 3);
private readonly TimeSpan AllowedRefreshInterval = new TimeSpan(0, 0, 3);
private GUIFrame menu;
@@ -31,12 +29,20 @@ namespace Barotrauma
private GUIButton joinButton;
private ServerInfo selectedServer;
private GUIButton scanServersButton;
//friends list
private GUILayoutGroup friendsButtonHolder;
private GUIButton friendsDropdownButton;
private GUIListBox friendsDropdown;
//Workshop downloads
private GUIFrame workshopDownloadsFrame = null;
private Steamworks.Ugc.Item? currentlyDownloadingWorkshopItem = null;
private Dictionary<ulong, Steamworks.Ugc.Item?> pendingWorkshopDownloads = null;
private string autoConnectName; private string autoConnectEndpoint;
private class FriendInfo
{
public UInt64 SteamID;
@@ -558,7 +564,7 @@ namespace Barotrauma
OnClicked = GameMain.MainMenuScreen.ReturnToMainMenu
};
new GUIButton(new RectTransform(new Vector2(0.25f, 0.9f), buttonContainer.RectTransform),
scanServersButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.9f), buttonContainer.RectTransform),
TextManager.Get("ServerListRefresh"))
{
OnClicked = (btn, userdata) => { RefreshServers(); return true; }
@@ -761,7 +767,7 @@ namespace Barotrauma
info.GameStarted = Screen.Selected != GameMain.NetLobbyScreen;
info.GameVersion = GameMain.Version.ToString();
info.MaxPlayers = serverSettings.MaxPlayers;
info.PlayStyle = PlayStyle.SomethingDifferent;
info.PlayStyle = serverSettings.PlayStyle;
info.RespondedToSteamQuery = true;
info.UsingWhiteList = serverSettings.Whitelist.Enabled;
info.TraitorsEnabled = serverSettings.TraitorsEnabled;
@@ -892,11 +898,11 @@ namespace Barotrauma
case "ServerListCompatible":
bool? s1Compatible = NetworkMember.IsCompatible(GameMain.Version.ToString(), s1.GameVersion);
if (!s1.ContentPackageHashes.Any()) { s1Compatible = null; }
if (s1Compatible.HasValue) { s1Compatible = s1Compatible.Value && s1.ContentPackagesMatch(GameMain.SelectedPackages); };
if (s1Compatible.HasValue) { s1Compatible = s1Compatible.Value && s1.ContentPackagesMatch(); };
bool? s2Compatible = NetworkMember.IsCompatible(GameMain.Version.ToString(), s2.GameVersion);
if (!s2.ContentPackageHashes.Any()) { s2Compatible = null; }
if (s2Compatible.HasValue) { s2Compatible = s2Compatible.Value && s2.ContentPackagesMatch(GameMain.SelectedPackages); };
if (s2Compatible.HasValue) { s2Compatible = s2Compatible.Value && s2.ContentPackagesMatch(); };
//convert to int to make sorting easier
//1 Compatible
@@ -946,6 +952,9 @@ namespace Barotrauma
public override void Deselect()
{
base.Deselect();
pendingWorkshopDownloads?.Clear();
workshopDownloadsFrame = null;
}
public override void Update(double deltaTime)
@@ -965,6 +974,43 @@ namespace Barotrauma
friendsDropdown.Visible = false;
}
}
if (currentlyDownloadingWorkshopItem?.IsInstalled ?? true)
{
if (pendingWorkshopDownloads?.Any() ?? false)
{
Steamworks.Ugc.Item? item = pendingWorkshopDownloads.Values.FirstOrDefault(it => it != null);
if (item != null)
{
ulong itemId = item.Value.Id;
currentlyDownloadingWorkshopItem = item;
SteamManager.SubscribeToWorkshopItem(itemId, () =>
{
pendingWorkshopDownloads.Remove(itemId);
if (SteamManager.CheckWorkshopItemInstalled(item))
{
SteamManager.UninstallWorkshopItem(item, false, out _);
}
if (SteamManager.InstallWorkshopItem(item, out string errorMsg, enableContentPackage: false, suppressInstallNotif: true))
{
workshopDownloadsFrame?.FindChild((c) => c.UserData is ulong l && l == itemId, true)?.Flash(GUI.Style.Green);
}
else
{
workshopDownloadsFrame?.FindChild((c) => c.UserData is ulong l && l == itemId, true)?.Flash(GUI.Style.Red);
DebugConsole.ThrowError(errorMsg);
}
});
}
}
else if (!string.IsNullOrEmpty(autoConnectEndpoint))
{
JoinServer(autoConnectEndpoint, autoConnectName);
autoConnectEndpoint = null;
}
}
}
private void FilterServers()
@@ -992,7 +1038,7 @@ namespace Barotrauma
else
{
bool incompatible =
(!serverInfo.ContentPackageHashes.Any() && serverInfo.ContentPackagesMatch(GameMain.Config.SelectedContentPackages)) ||
(!serverInfo.ContentPackageHashes.Any() && serverInfo.ContentPackagesMatch()) ||
(remoteVersion != null && !NetworkMember.IsCompatible(GameMain.Version, remoteVersion));
child.Visible =
@@ -1018,7 +1064,7 @@ namespace Barotrauma
{
var playStyle = (PlayStyle)tickBox.UserData;
if (!tickBox.Selected && serverInfo.PlayStyle == playStyle)
if (!tickBox.Selected && (serverInfo.PlayStyle == playStyle || !serverInfo.PlayStyle.HasValue))
{
child.Visible = false;
break;
@@ -1136,7 +1182,7 @@ namespace Barotrauma
Port = port.ToString(),
QueryPort = NetConfig.DefaultQueryPort.ToString(),
GameVersion = GameMain.Version.ToString(),
PlayStyle = PlayStyle.Serious
PlayStyle = null
};
var serverFrame = serverList.Content.FindChild(d => (d.UserData is ServerInfo info) &&
@@ -1546,6 +1592,7 @@ namespace Barotrauma
{
CanBeFocused = false
};
scanServersButton.Enabled = false;
}
else
{
@@ -1555,6 +1602,7 @@ namespace Barotrauma
AddToServerList(info);
QueueInfoQuery(info);
}
scanServersButton.Enabled = true;
}
}
else
@@ -1712,7 +1760,7 @@ namespace Barotrauma
CanBeFocused = false,
Selected =
(NetworkMember.IsCompatible(GameMain.Version.ToString(), serverInfo.GameVersion) ?? true) &&
serverInfo.ContentPackagesMatch(GameMain.SelectedPackages),
serverInfo.ContentPackagesMatch(),
UserData = "compatible"
};
@@ -1818,19 +1866,43 @@ namespace Barotrauma
for (int i = 0; i < serverInfo.ContentPackageNames.Count; i++)
{
if (!GameMain.SelectedPackages.Any(cp => cp.MD5hash.Hash == serverInfo.ContentPackageHashes[i]))
bool listAsIncompatible = false;
if (serverInfo.ContentPackageWorkshopIds[i] == 0)
{
listAsIncompatible = !GameMain.Config.AllEnabledPackages.Any(cp => cp.MD5hash.Hash == serverInfo.ContentPackageHashes[i]);
}
else
{
listAsIncompatible = GameMain.Config.AllEnabledPackages.Any(cp => cp.MD5hash.Hash != serverInfo.ContentPackageHashes[i] &&
cp.SteamWorkshopId == serverInfo.ContentPackageWorkshopIds[i]);
}
if (listAsIncompatible)
{
if (toolTip != "") toolTip += "\n";
toolTip += TextManager.GetWithVariables("ServerListIncompatibleContentPackage", new string[2] { "[contentpackage]", "[hash]" },
new string[2] { serverInfo.ContentPackageNames[i], Md5Hash.GetShortHash(serverInfo.ContentPackageHashes[i]) });
}
}
serverContent.Children.ForEach(c => c.ToolTip = toolTip);
serverName.TextColor *= 0.5f;
serverPlayers.TextColor *= 0.5f;
}
else
{
string toolTip = "";
for (int i = 0; i < serverInfo.ContentPackageNames.Count; i++)
{
if (!GameMain.Config.AllEnabledPackages.Any(cp => cp.MD5hash.Hash == serverInfo.ContentPackageHashes[i]))
{
if (toolTip != "") toolTip += "\n";
toolTip += TextManager.GetWithVariable("ServerListIncompatibleContentPackageWorkshopAvailable", "[contentpackage]", serverInfo.ContentPackageNames[i]);
break;
}
}
serverContent.Children.ForEach(c => c.ToolTip = toolTip);
}
serverContent.Recalculate();
@@ -1921,17 +1993,17 @@ namespace Barotrauma
serverList.ClearChildren();
new GUIMessageBox(TextManager.Get("MasterServerErrorLabel"), TextManager.GetWithVariable("MasterServerErrorException", "[error]", masterServerResponse.ErrorException.ToString()));
}
else if (masterServerResponse.StatusCode != System.Net.HttpStatusCode.OK)
else if (masterServerResponse.StatusCode != HttpStatusCode.OK)
{
serverList.ClearChildren();
switch (masterServerResponse.StatusCode)
{
case System.Net.HttpStatusCode.NotFound:
case HttpStatusCode.NotFound:
new GUIMessageBox(TextManager.Get("MasterServerErrorLabel"),
TextManager.GetWithVariable("MasterServerError404", "[masterserverurl]", NetConfig.MasterServerUrl));
break;
case System.Net.HttpStatusCode.ServiceUnavailable:
case HttpStatusCode.ServiceUnavailable:
new GUIMessageBox(TextManager.Get("MasterServerErrorLabel"),
TextManager.Get("MasterServerErrorUnavailable"));
break;
@@ -1958,6 +2030,79 @@ namespace Barotrauma
masterServerResponded = true;
}
public void DownloadWorkshopItems(IEnumerable<ulong> ids, string serverName, string endPointString)
{
if (workshopDownloadsFrame != null) { return; }
int rowCount = ids.Count() + 2;
autoConnectName = serverName; autoConnectEndpoint = endPointString;
workshopDownloadsFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), null, Color.Black * 0.5f);
pendingWorkshopDownloads = new Dictionary<ulong, Steamworks.Ugc.Item?>();
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f + 0.03f * rowCount), workshopDownloadsFrame.RectTransform, Anchor.Center, Pivot.Center));
var innerLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, (float)rowCount / (float)(rowCount + 3)), innerFrame.RectTransform, Anchor.Center, Pivot.Center));
foreach (ulong id in ids)
{
pendingWorkshopDownloads.Add(id, null);
var itemLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f / rowCount), innerLayout.RectTransform), true, Anchor.CenterLeft)
{
UserData = id
};
TaskPool.Add("RetrieveWorkshopItemData", Steamworks.SteamUGC.QueryFileAsync(id), (t) =>
{
if (t.IsFaulted)
{
TaskPool.PrintTaskExceptions(t, $"Failed to retrieve Workshop item info (ID {id})");
return;
}
Steamworks.Ugc.Item? item = ((Task<Steamworks.Ugc.Item?>)t).Result;
if (!item.HasValue)
{
DebugConsole.ThrowError($"Failed to find a Steam Workshop item with the ID {id}.");
return;
}
if (pendingWorkshopDownloads.ContainsKey(id))
{
pendingWorkshopDownloads[id] = item;
new GUITextBlock(new RectTransform(new Vector2(0.4f, 0.67f), itemLayout.RectTransform, Anchor.CenterLeft, Pivot.CenterLeft), item.Value.Title);
new GUIProgressBar(new RectTransform(new Vector2(0.6f, 0.67f), itemLayout.RectTransform, Anchor.CenterLeft, Pivot.CenterLeft), 0f, Color.Lime)
{
ProgressGetter = () =>
{
if (item.Value.IsInstalled) { return 1.0f; }
else if (!item.Value.IsDownloading) { return 0.0f; }
return item.Value.DownloadAmount;
}
};
}
});
}
var buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 2.0f / rowCount), innerLayout.RectTransform), true, Anchor.CenterLeft)
{
UserData = "buttons"
};
new GUIButton(new RectTransform(new Vector2(0.3f, 0.67f), buttonLayout.RectTransform, Anchor.CenterLeft, Pivot.CenterLeft), TextManager.Get("Cancel"))
{
OnClicked = (btn, obj) =>
{
autoConnectEndpoint = null;
autoConnectName = null;
pendingWorkshopDownloads.Clear();
workshopDownloadsFrame = null;
return true;
}
};
}
private bool JoinServer(string endpoint, string serverName)
{
if (string.IsNullOrWhiteSpace(ClientNameBox.Text))
@@ -2113,6 +2258,8 @@ namespace Barotrauma
friendPopup?.AddToGUIUpdateList();
friendsDropdown?.AddToGUIUpdateList();
workshopDownloadsFrame?.AddToGUIUpdateList();
}
}

View File

@@ -325,7 +325,7 @@ namespace Barotrauma
{
loadedSprites.ForEach(s => s.Remove());
loadedSprites.Clear();
var contentPackages = GameMain.Config.SelectedContentPackages.ToList();
var contentPackages = GameMain.Config.AllEnabledPackages.ToList();
#if !DEBUG
var vanilla = GameMain.VanillaContent;

View File

@@ -1,12 +1,11 @@
using Barotrauma.Steam;
using Barotrauma.IO;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using RestSharp;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Barotrauma
@@ -44,7 +43,7 @@ namespace Barotrauma
public int PendingLoads = 1;
}
private readonly Dictionary<ulong, PendingPreviewImageDownload> pendingPreviewImageDownloads = new Dictionary<ulong, PendingPreviewImageDownload>();
private Dictionary<string, Sprite> itemPreviewSprites = new Dictionary<string, Sprite>();
private readonly Dictionary<string, Sprite> itemPreviewSprites = new Dictionary<string, Sprite>();
private enum Tab
{
@@ -237,7 +236,7 @@ namespace Barotrauma
SelectTab(Tab.Mods);
subscribedCoroutine = CoroutineManager.StartCoroutine(PollSubscribedItems());
CoroutineManager.StartCoroutine(PollSubscribedItems());
}
private GUITextBox CreateFilterBox(GUIComponent parent, GUIListBox listbox)
@@ -283,7 +282,7 @@ namespace Barotrauma
RefreshSubscribedItems();
}
CoroutineHandle subscribedCoroutine;
float subscribePollAdditionalWait = 0.0f;
private IEnumerable<object> PollSubscribedItems()
{
@@ -293,6 +292,13 @@ namespace Barotrauma
while (true)
{
while (CoroutineManager.IsCoroutineRunning("Load")) { yield return new WaitForSeconds(1.0f); }
while (subscribePollAdditionalWait > 0.01f)
{
subscribePollAdditionalWait = Math.Min(subscribePollAdditionalWait, 3.0f);
float wait = subscribePollAdditionalWait;
yield return new WaitForSeconds(wait);
subscribePollAdditionalWait -= wait;
}
uint newNumSubscribed = Steamworks.SteamUGC.NumSubscribedItems;
if (newNumSubscribed != numSubscribed)
{
@@ -338,15 +344,6 @@ namespace Barotrauma
}
}
public void SubscribeToPackages(List<string> packageUrls)
{
foreach (string url in packageUrls)
{
SteamManager.SubscribeToWorkshopItem(url);
}
GameMain.SteamWorkshopScreen.Select();
}
public IEnumerable<object> RefreshDownloadState()
{
bool isDownloading = true;
@@ -420,14 +417,14 @@ namespace Barotrauma
continue;
}
//ignore subs that are part of a workshop content package
if (ContentPackage.List.Any(cp => !string.IsNullOrEmpty(cp.SteamWorkshopUrl) &&
if (ContentPackage.AllPackages.Any(cp => cp.SteamWorkshopId != 0 &&
cp.Files.Any(f => f.Type == ContentType.Submarine && Path.GetFullPath(f.Path) == subPath)))
{
continue;
}
//ignore subs that are defined in a content package with more files than just the sub
//(these will be listed in the "content packages" section)
if (ContentPackage.List.Any(cp => cp.Files.Count > 1 &&
if (ContentPackage.AllPackages.Any(cp => cp.Files.Count > 1 &&
cp.Files.Any(f => f.Type == ContentType.Submarine && Path.GetFullPath(f.Path) == subPath)))
{
continue;
@@ -441,9 +438,9 @@ namespace Barotrauma
{
CanBeFocused = false
};
foreach (ContentPackage contentPackage in ContentPackage.List)
foreach (ContentPackage contentPackage in ContentPackage.AllPackages)
{
if (!string.IsNullOrEmpty(contentPackage.SteamWorkshopUrl) || contentPackage.HideInWorkshopMenu) { continue; }
if (contentPackage.SteamWorkshopId != 0 || contentPackage.HideInWorkshopMenu) { continue; }
if (contentPackage == GameMain.VanillaContent) { continue; }
//don't list content packages that only define one sub (they're visible in the "Submarines" section)
if (contentPackage.Files.Count == 1 && contentPackage.Files[0].Type == ContentType.Submarine) { continue; }
@@ -488,7 +485,7 @@ namespace Barotrauma
text = topItemFilter.Text;
}
bool visible = string.IsNullOrEmpty(text) ? true : (item?.Title?.ToLower().Contains(text.ToLower()) ?? false);
bool visible = string.IsNullOrEmpty(text) || (item?.Title?.ToLower().Contains(text.ToLower()) ?? false);
int prevIndex = -1;
var existingFrame = listBox.Content.FindChild((component) => { return (component.UserData is Steamworks.Ugc.Item?) && (component.UserData as Steamworks.Ugc.Item?)?.Id == item?.Id; });
@@ -611,7 +608,7 @@ namespace Barotrauma
if ((item?.IsSubscribed ?? false) && (item?.IsInstalled ?? false) && Directory.Exists(item?.Directory))
{
bool installed = SteamManager.CheckWorkshopItemEnabled(item);
bool installed = SteamManager.CheckWorkshopItemInstalled(item);
if (!installed)
{
@@ -626,7 +623,7 @@ namespace Barotrauma
}
else
{
installed = SteamManager.EnableWorkShopItem(item, out string errorMsg, Selected == this);
installed = SteamManager.InstallWorkshopItem(item, out string errorMsg, Selected == this);
if (!installed)
{
DebugConsole.NewMessage(errorMsg, Color.Red);
@@ -660,7 +657,7 @@ namespace Barotrauma
{
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.5f), rightColumn.RectTransform), TextManager.Get("WorkshopItemDownloadPending"));
}
else if (!(item?.IsSubscribed ?? false))
else if (!(item?.IsSubscribed ?? false) && (listBox != subscribedItemList))
{
var downloadBtn = new GUIButton(new RectTransform(new Point((int)(32 * GUI.Scale)), rightColumn.RectTransform), "", style: "GUIPlusButton")
{
@@ -684,9 +681,9 @@ namespace Barotrauma
var elem = subscribedItemList.Content.GetChildByUserData(item);
try
{
bool reselect = GameMain.Config.SelectedContentPackages.Any(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && cp.SteamWorkshopUrl == item?.Url);
if (!SteamManager.DisableWorkShopItem(item, false, out string errorMsg) ||
!SteamManager.EnableWorkShopItem(item, out errorMsg, reselect, true))
bool reselect = GameMain.Config.AllEnabledPackages.Any(cp => cp.SteamWorkshopId != 0 && cp.SteamWorkshopId == item?.Id);
if (!SteamManager.UninstallWorkshopItem(item, false, out string errorMsg) ||
!SteamManager.InstallWorkshopItem(item, out errorMsg, reselect, true))
{
DebugConsole.ThrowError($"Failed to reinstall \"{item?.Title}\": {errorMsg}", null, true);
elem.Flash(GUI.Style.Red);
@@ -707,8 +704,9 @@ namespace Barotrauma
};
unsubBtn.OnClicked = (btn, userdata) =>
{
SteamManager.DisableWorkShopItem(item, true, out _);
subscribePollAdditionalWait += 1.0f;
item?.Unsubscribe();
SteamManager.UninstallWorkshopItem(item, true, out _);
subscribedItemList.RemoveChild(subscribedItemList.Content.GetChildByUserData(item));
return true;
};
@@ -819,31 +817,34 @@ namespace Barotrauma
new Tuple<Steamworks.Ugc.Item?, GUIListBox>(item, listBox),
(task, tuple) =>
{
(var it, var lb) = tuple;
var previewImage = lb.Content.FindChild(item)?.GetChildByUserData("previewimage") as GUIImage;
if (previewImage != null)
//must be done in the main thread because creating/removing GUI elements is not thread-safe
CrossThread.RequestExecutionOnMainThread(() =>
{
previewImage.Sprite = ((Task<Sprite>)task).Result;
}
else
{
CreateWorkshopItemFrame(it, lb);
}
(var it, var lb) = tuple;
if (lb.Content.FindChild(item)?.GetChildByUserData("previewimage") is GUIImage previewImage)
{
previewImage.Sprite = ((Task<Sprite>)task).Result;
}
else
{
CreateWorkshopItemFrame(it, lb);
}
if (modsPreviewFrame.FindChild(it) != null)
{
ShowItemPreview(it, modsPreviewFrame);
}
if (browsePreviewFrame.FindChild(item) != null)
{
ShowItemPreview(it, browsePreviewFrame);
}
if (modsPreviewFrame.FindChild(it) != null)
{
ShowItemPreview(it, modsPreviewFrame);
}
if (browsePreviewFrame.FindChild(item) != null)
{
ShowItemPreview(it, browsePreviewFrame);
}
lock (pendingPreviewImageDownloads)
{
pendingPreviewImageDownloads[it.Value.Id].PendingLoads--;
if (pendingPreviewImageDownloads[it.Value.Id].PendingLoads <= 0) { pendingPreviewImageDownloads.Remove(it.Value.Id); }
}
lock (pendingPreviewImageDownloads)
{
pendingPreviewImageDownloads[it.Value.Id].PendingLoads--;
if (pendingPreviewImageDownloads[it.Value.Id].PendingLoads <= 0) { pendingPreviewImageDownloads.Remove(it.Value.Id); }
}
});
});
}
@@ -872,15 +873,13 @@ namespace Barotrauma
{
if (item == null) { return false; }
if (!(item?.IsSubscribed ?? false)) { item?.Subscribe(); }
var parentElement = downloadButton.Parent;
parentElement.RemoveChild(downloadButton);
var textBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.5f), parentElement.RectTransform), TextManager.Get("WorkshopItemDownloading"));
item?.Download(onInstalled: () =>
SteamManager.SubscribeToWorkshopItem(item.Value.Id, () =>
{
if (SteamManager.EnableWorkShopItem(item, out _))
if (SteamManager.InstallWorkshopItem(item, out _))
{
textBlock.Text = TextManager.Get("workshopiteminstalled");
frame.Flash(GUI.Style.Green);
@@ -1022,8 +1021,9 @@ namespace Barotrauma
UserData = item,
OnClicked = (btn, userdata) =>
{
SteamManager.DisableWorkShopItem(item, true, out _);
subscribePollAdditionalWait += 1.0f;
item?.Unsubscribe();
SteamManager.UninstallWorkshopItem(item, true, out _);
subscribedItemList.RemoveChild(subscribedItemList.Content.GetChildByUserData(item));
itemPreviewFrame.ClearChildren();
return true;
@@ -1294,7 +1294,7 @@ namespace Barotrauma
new GUITickBox(new RectTransform(new Vector2(1.0f, 0.1f), topLeftColumn.RectTransform), TextManager.Get("WorkshopItemCorePackage"))
{
ToolTip = TextManager.Get("WorkshopItemCorePackageTooltip"),
Selected = itemContentPackage.CorePackage,
Selected = itemContentPackage.IsCorePackage,
OnSelected = (tickbox) =>
{
if (tickbox.Selected)
@@ -1309,12 +1309,12 @@ namespace Barotrauma
}
else
{
itemContentPackage.CorePackage = tickbox.Selected;
itemContentPackage.IsCorePackage = tickbox.Selected;
}
}
else
{
itemContentPackage.CorePackage = false;
itemContentPackage.IsCorePackage = false;
}
return true;
}
@@ -1478,7 +1478,7 @@ namespace Barotrauma
SelectTab(Tab.Browse);
deleteVerification.Close();
createItemFrame.ClearChildren();
itemContentPackage.SteamWorkshopUrl = "";
itemContentPackage.SteamWorkshopId = 0;
itemContentPackage.Save(itemContentPackage.Path);
return true;
};
@@ -1535,9 +1535,17 @@ namespace Barotrauma
return;
}
if (filePath != previewImagePath)
if (Path.GetFullPath(filePath) != previewImagePath)
{
File.Copy(filePath, previewImagePath, overwrite: true);
try
{
File.Copy(filePath, previewImagePath, overwrite: true);
}
catch (System.IO.IOException e)
{
DebugConsole.ThrowError("Failed to copy the preview image \"{previewImagePath}\" to the mod folder.", e);
return;
}
}
if (itemPreviewSprites.ContainsKey(previewImagePath))
@@ -1560,13 +1568,12 @@ namespace Barotrauma
string modFolder = Path.GetDirectoryName(itemContentPackage.Path);
string filePathRelativeToModFolder = UpdaterUtil.GetRelativePath(file, Path.Combine(Environment.CurrentDirectory, modFolder));
string destinationPath;
//file is not inside the mod folder, we need to move it
if (filePathRelativeToModFolder.StartsWith("..") ||
Path.GetPathRoot(Environment.CurrentDirectory) != Path.GetPathRoot(file))
{
destinationPath = Path.Combine(modFolder, Path.GetFileName(file));
string destinationPath = Path.Combine(modFolder, Path.GetFileName(file));
//add a number to the filename if a file with the same name already exists
i = 2;
while (File.Exists(destinationPath))
@@ -1582,11 +1589,7 @@ namespace Barotrauma
{
DebugConsole.ThrowError("Copying the file \"" + file + "\" to the mod folder failed.", e);
return;
}
}
else
{
destinationPath = Path.Combine(modFolder, filePathRelativeToModFolder);
}
}
}
RefreshCreateItemFileList();
@@ -1605,7 +1608,7 @@ namespace Barotrauma
if (itemContentPackage == null) return;
var contentTypes = Enum.GetValues(typeof(ContentType));
List<ContentFile> files = itemContentPackage.Files.ToList();
List<ContentFile> files = itemContentPackage.FilesUnsaved.ToList();
for (int i = files.Count - 1; i >= 0; i--)
{
@@ -1613,15 +1616,14 @@ namespace Barotrauma
bool fileExists = File.Exists(contentFile.Path);
if (contentFile.Type == ContentType.Executable ||
contentFile.Type == ContentType.ServerExecutable)
if (contentFile.Type == ContentType.ServerExecutable)
{
fileExists |= File.Exists(Path.GetFileNameWithoutExtension(contentFile.Path) + ".dll");
}
if (!fileExists)
{
itemContentPackage.Files.Remove(contentFile);
itemContentPackage.RemoveFile(contentFile);
files.RemoveAt(i);
}
}
@@ -1653,8 +1655,7 @@ namespace Barotrauma
bool illegalPath = !ContentPackage.IsModFilePathAllowed(contentFile);
bool fileExists = File.Exists(contentFile.Path);
if (contentFile.Type == ContentType.Executable ||
contentFile.Type == ContentType.ServerExecutable)
if (contentFile.Type == ContentType.ServerExecutable)
{
fileExists |= File.Exists(Path.GetFileNameWithoutExtension(contentFile.Path) + ".dll");
}
@@ -1674,7 +1675,7 @@ namespace Barotrauma
var tickBox = new GUITickBox(new RectTransform(Vector2.One, content.RectTransform, scaleBasis: ScaleBasis.BothHeight), "")
{
Selected = itemContentPackage.Files.Contains(contentFile),
Selected = itemContentPackage.FilesUnsaved.Contains(contentFile),
UserData = contentFile
};
@@ -1683,11 +1684,11 @@ namespace Barotrauma
ContentFile f = tb.UserData as ContentFile;
if (tb.Selected)
{
if (!itemContentPackage.Files.Contains(f)) { itemContentPackage.Files.Add(f); }
if (!itemContentPackage.FilesUnsaved.Contains(f)) { itemContentPackage.AddFile(f); }
}
else
{
if (itemContentPackage.Files.Contains(f)) { itemContentPackage.Files.Remove(f); }
if (itemContentPackage.FilesUnsaved.Contains(f)) { itemContentPackage.RemoveFile(f); }
}
return true;
@@ -1702,7 +1703,7 @@ namespace Barotrauma
nameText.TextColor = GUI.Style.Red;
tickBox.ToolTip = TextManager.Get("WorkshopItemFileNotFound");
}
else if (illegalPath && !ContentPackage.List.Any(cp => cp.Files.Any(f => Path.GetFullPath(f.Path) == Path.GetFullPath(contentFile.Path))))
else if (illegalPath && !ContentPackage.AllPackages.Any(cp => cp.FilesUnsaved.Any(f => Path.GetFullPath(f.Path) == Path.GetFullPath(contentFile.Path))))
{
nameText.TextColor = GUI.Style.Red;
tickBox.ToolTip = TextManager.Get("WorkshopItemIllegalPath");
@@ -1839,7 +1840,10 @@ namespace Barotrauma
{
new GUIMessageBox(
TextManager.Get("Error"),
TextManager.GetWithVariable("WorkshopItemPublishFailed", "[itemname]", item?.Title) + " Task ended with status "+workshopPublishStatus?.TaskStatus?.ToString());
TextManager.GetWithVariable("WorkshopItemPublishFailed", "[itemname]", item?.Title) +
(workshopPublishStatus?.TaskStatus != null ?
" Task ended with status " +workshopPublishStatus?.TaskStatus?.ToString() :
" Publish failed with result "+ workshopPublishStatus.Result?.Result.ToString()));
}
else
{

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