v0.19.8.0

This commit is contained in:
Juan Pablo Arce
2022-09-28 21:30:52 -03:00
parent fec8131243
commit 3ca584f2fc
152 changed files with 1931 additions and 1071 deletions
@@ -399,12 +399,9 @@ namespace Barotrauma
progressBar.Draw(spriteBatch, cam); progressBar.Draw(spriteBatch, cam);
} }
void DrawInteractionIcon(Entity entity, string iconStyle) void DrawInteractionIcon(Entity entity, Identifier iconStyle)
{ {
if (entity == null || entity.Removed) { return; } if (entity == null || entity.Removed) { return; }
var characterEntity = entity as Character;
if (characterEntity is not null && (characterEntity.IsDead || characterEntity.IsIncapacitated)) { return; }
if (GUIStyle.GetComponentStyle(iconStyle) is not GUIComponentStyle style) { return; }
Hull currentHull = entity switch Hull currentHull = entity switch
{ {
@@ -412,20 +409,28 @@ namespace Barotrauma
Item item => item.CurrentHull, Item item => item.CurrentHull,
_ => null _ => null
}; };
Range<float> visibleRange = new Range<float>(currentHull == Character.Controlled.CurrentHull ? 500.0f : 100.0f, float.PositiveInfinity); Range<float> visibleRange = new Range<float>(currentHull == Character.Controlled.CurrentHull ? 500.0f : 100.0f, float.PositiveInfinity);
if (characterEntity?.CampaignInteractionType == CampaignMode.InteractionType.Examine) LocalizedString label = null;
if (entity is Character characterEntity)
{ {
//TODO: we could probably do better than just hardcoding if (characterEntity.IsDead || characterEntity.IsIncapacitated) { return; }
//a check for InteractionType.Examine here. if (characterEntity?.CampaignInteractionType == CampaignMode.InteractionType.Examine)
{
//TODO: we could probably do better than just hardcoding
//a check for InteractionType.Examine here.
if (Vector2.DistanceSquared(character.Position, entity.Position) > 500f * 500f) { return; } if (Vector2.DistanceSquared(character.Position, entity.Position) > 500f * 500f) { return; }
var body = Submarine.CheckVisibility(character.SimPosition, entity.SimPosition, ignoreLevel: true); var body = Submarine.CheckVisibility(character.SimPosition, entity.SimPosition, ignoreLevel: true);
if (body != null && body.UserData != entity) { return; } if (body != null && body.UserData != entity) { return; }
visibleRange = new Range<float>(-100f, 500f); visibleRange = new Range<float>(-100f, 500f);
}
label = characterEntity?.Info?.Title;
} }
if (GUIStyle.GetComponentStyle(iconStyle) is not GUIComponentStyle style) { return; }
float dist = Vector2.Distance(character.WorldPosition, entity.WorldPosition); float dist = Vector2.Distance(character.WorldPosition, entity.WorldPosition);
float distFactor = 1.0f - MathUtils.InverseLerp(1000.0f, 3000.0f, dist); float distFactor = 1.0f - MathUtils.InverseLerp(1000.0f, 3000.0f, dist);
float alpha = MathHelper.Lerp(0.3f, 1.0f, distFactor); float alpha = MathHelper.Lerp(0.3f, 1.0f, distFactor);
@@ -436,13 +441,13 @@ namespace Barotrauma
visibleRange, visibleRange,
style.GetDefaultSprite(), style.GetDefaultSprite(),
style.Color * alpha, style.Color * alpha,
label: characterEntity?.Info?.Title); label: label);
} }
foreach (Character npc in Character.CharacterList) foreach (Character npc in Character.CharacterList)
{ {
if (npc.CampaignInteractionType == CampaignMode.InteractionType.None) { continue; } if (npc.CampaignInteractionType == CampaignMode.InteractionType.None) { continue; }
DrawInteractionIcon(npc, "CampaignInteractionIcon." + npc.CampaignInteractionType); DrawInteractionIcon(npc, ("CampaignInteractionIcon." + npc.CampaignInteractionType).ToIdentifier());
} }
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial is not null) if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial is not null)
@@ -2,6 +2,7 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
namespace Barotrauma namespace Barotrauma
@@ -141,7 +142,7 @@ namespace Barotrauma
public virtual void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null) public virtual void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{ {
if (!(extraData is IEventData eventData)) { throw new Exception($"Malformed character event: expected {nameof(Character)}.{nameof(IEventData)}"); } if (extraData is not IEventData eventData) { throw new Exception($"Malformed character event: expected {nameof(Character)}.{nameof(IEventData)}"); }
msg.WriteRangedInteger((int)eventData.EventType, (int)EventType.MinValue, (int)EventType.MaxValue); msg.WriteRangedInteger((int)eventData.EventType, (int)EventType.MinValue, (int)EventType.MaxValue);
switch (eventData) switch (eventData)
@@ -471,25 +472,11 @@ namespace Barotrauma
break; break;
case EventType.AddToCrew: case EventType.AddToCrew:
GameMain.GameSession.CrewManager.AddCharacter(this); GameMain.GameSession.CrewManager.AddCharacter(this);
CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte(); ReadItemTeamChange(msg, true);
ushort itemCount = msg.ReadUInt16(); break;
for (int i = 0; i < itemCount; i++) case EventType.RemoveFromCrew:
{ GameMain.GameSession.CrewManager.RemoveCharacter(this, removeInfo: true);
ushort itemID = msg.ReadUInt16(); ReadItemTeamChange(msg, false);
if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; }
item.AllowStealing = true;
var wifiComponent = item.GetComponent<WifiComponent>();
if (wifiComponent != null)
{
wifiComponent.TeamID = teamID;
}
var idCard = item.GetComponent<IdCard>();
if (idCard != null)
{
idCard.TeamID = teamID;
idCard.SubmarineSpecificID = 0;
}
}
break; break;
case EventType.UpdateExperience: case EventType.UpdateExperience:
int experienceAmount = msg.ReadInt32(); int experienceAmount = msg.ReadInt32();
@@ -520,9 +507,27 @@ namespace Barotrauma
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true); info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
} }
break; break;
} }
msg.ReadPadBits(); msg.ReadPadBits();
static void ReadItemTeamChange(IReadMessage msg, bool allowStealing)
{
var itemTeamChange = INetSerializableStruct.Read<ItemTeamChange>(msg);
foreach (var itemID in itemTeamChange.ItemIds)
{
if (FindEntityByID(itemID) is not Item item) { continue; }
item.AllowStealing = allowStealing;
if (item.GetComponent<WifiComponent>() is { } wifiComponent)
{
wifiComponent.TeamID = itemTeamChange.TeamId;
}
if (item.GetComponent<IdCard>() is { } idCard)
{
idCard.TeamID = itemTeamChange.TeamId;
idCard.SubmarineSpecificID = 0;
}
}
}
} }
public static Character ReadSpawnData(IReadMessage inc) public static Character ReadSpawnData(IReadMessage inc)
@@ -280,6 +280,7 @@ namespace Barotrauma
cprButton = new GUIButton(new RectTransform(new Vector2(0.17f, 0.17f), characterIndicatorArea.RectTransform, Anchor.BottomLeft, scaleBasis: ScaleBasis.Smallest), text: "", style: "CPRButton") cprButton = new GUIButton(new RectTransform(new Vector2(0.17f, 0.17f), characterIndicatorArea.RectTransform, Anchor.BottomLeft, scaleBasis: ScaleBasis.Smallest), text: "", style: "CPRButton")
{ {
UserData = UIHighlightAction.ElementId.CPRButton,
OnClicked = (button, userData) => OnClicked = (button, userData) =>
{ {
Character selectedCharacter = Character.Controlled?.SelectedCharacter; Character selectedCharacter = Character.Controlled?.SelectedCharacter;
@@ -690,7 +691,7 @@ namespace Barotrauma
{ {
distortTimer = (distortTimer + deltaTime * distortSpeed) % MathHelper.TwoPi; distortTimer = (distortTimer + deltaTime * distortSpeed) % MathHelper.TwoPi;
Character.BlurStrength = (float)(Math.Sin(distortTimer) + 1.5f) * 0.25f * blurStrength; Character.BlurStrength = (float)(Math.Sin(distortTimer) + 1.5f) * 0.25f * blurStrength;
Character.DistortStrength = (float)(Math.Sin(distortTimer) + 1.0f) * 0.1f * distortStrength; Character.DistortStrength = (float)(Math.Sin(distortTimer) + 1.0f) * 0.05f * distortStrength;
} }
else else
{ {
@@ -1294,6 +1295,7 @@ namespace Barotrauma
Dictionary<Identifier, float> treatmentSuitability = new Dictionary<Identifier, float>(); Dictionary<Identifier, float> treatmentSuitability = new Dictionary<Identifier, float>();
GetSuitableTreatments(treatmentSuitability, GetSuitableTreatments(treatmentSuitability,
normalize: true, normalize: true,
user: Character.Controlled,
ignoreHiddenAfflictions: true, ignoreHiddenAfflictions: true,
limb: selectedLimbIndex == -1 ? null : Character.AnimController.Limbs.Find(l => l.HealthIndex == selectedLimbIndex)); limb: selectedLimbIndex == -1 ? null : Character.AnimController.Limbs.Find(l => l.HealthIndex == selectedLimbIndex));
@@ -1343,7 +1345,7 @@ namespace Barotrauma
PlaySoundOnSelect = false, PlaySoundOnSelect = false,
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
if (!(userdata is ItemPrefab itemPrefab)) { return false; } if (userdata is not ItemPrefab itemPrefab) { return false; }
var item = Character.Controlled.Inventory.FindItem(it => it.Prefab == itemPrefab, recursive: true); var item = Character.Controlled.Inventory.FindItem(it => it.Prefab == itemPrefab, recursive: true);
if (item == null) { return false; } if (item == null) { return false; }
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex); Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex);
@@ -1900,6 +1902,7 @@ namespace Barotrauma
public void ClientRead(IReadMessage inc) public void ClientRead(IReadMessage inc)
{ {
newAfflictions.Clear(); newAfflictions.Clear();
newPeriodicEffects.Clear();
byte afflictionCount = inc.ReadByte(); byte afflictionCount = inc.ReadByte();
for (int i = 0; i < afflictionCount; i++) for (int i = 0; i < afflictionCount; i++)
{ {
@@ -1921,7 +1924,7 @@ namespace Barotrauma
int periodicAfflictionCount = inc.ReadByte(); int periodicAfflictionCount = inc.ReadByte();
for (int j = 0; j < periodicAfflictionCount; j++) for (int j = 0; j < periodicAfflictionCount; j++)
{ {
float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8); float periodicAfflictionTimer = inc.ReadRangedSingle(0, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
newPeriodicEffects.Add((afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer)); newPeriodicEffects.Add((afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
} }
newAfflictions.Add((null, afflictionPrefab, afflictionStrength)); newAfflictions.Add((null, afflictionPrefab, afflictionStrength));
@@ -1991,13 +1994,13 @@ namespace Barotrauma
//timer has wrapped around, apply the effect //timer has wrapped around, apply the effect
if (periodicEffect.timer - existingAffliction.PeriodicEffectTimers[periodicEffect.effect] > periodicEffect.effect.MinInterval / 2) if (periodicEffect.timer - existingAffliction.PeriodicEffectTimers[periodicEffect.effect] > periodicEffect.effect.MinInterval / 2)
{ {
existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer;
foreach (StatusEffect effect in periodicEffect.effect.StatusEffects) foreach (StatusEffect effect in periodicEffect.effect.StatusEffects)
{ {
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == limbHealths.IndexOf(limb)); Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == limbHealths.IndexOf(limb));
existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: targetLimb); existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: targetLimb);
} }
} }
existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer;
} }
} }
@@ -92,11 +92,13 @@ namespace Barotrauma
} }
} }
float prevSize = conversationList.TotalSize;
List<GUIButton> extraButtons = CreateConversation(conversationList, text, speaker, options, string.IsNullOrWhiteSpace(spriteIdentifier)); List<GUIButton> extraButtons = CreateConversation(conversationList, text, speaker, options, string.IsNullOrWhiteSpace(spriteIdentifier));
AssignActionsToButtons(extraButtons, lastMessageBox); AssignActionsToButtons(extraButtons, lastMessageBox);
RecalculateLastMessage(conversationList, true); RecalculateLastMessage(conversationList, true);
conversationList.BarScroll = (prevSize - conversationList.Content.Rect.Height) / (conversationList.TotalSize - conversationList.Content.Rect.Height);
conversationList.ScrollToEnd(0.5f); conversationList.ScrollToEnd(duration: 0.5f);
lastMessageBox.SetBackgroundIcon(eventSprite); lastMessageBox.SetBackgroundIcon(eventSprite);
return; return;
} }
@@ -112,7 +114,7 @@ namespace Barotrauma
}; };
messageBox.OnAddedToGUIUpdateList += (GUIComponent component) => messageBox.OnAddedToGUIUpdateList += (GUIComponent component) =>
{ {
if (!(Screen.Selected is GameScreen)) { messageBox.Close(); } if (Screen.Selected is not GameScreen) { messageBox.Close(); }
}; };
lastMessageBox = messageBox; lastMessageBox = messageBox;
@@ -321,7 +323,7 @@ namespace Barotrauma
AlwaysOverrideCursor = true AlwaysOverrideCursor = true
}; };
LocalizedString translatedText = TextManager.Get(text).Fallback(text); LocalizedString translatedText = TextManager.ParseInputTypes(TextManager.Get(text)).Fallback(text);
if (speaker?.Info != null && drawChathead) if (speaker?.Info != null && drawChathead)
{ {
@@ -0,0 +1,58 @@
using Microsoft.Xna.Framework;
using System.Linq;
namespace Barotrauma;
partial class InventoryHighlightAction : EventAction
{
private static readonly Color highlightColor = Color.Orange;
partial void UpdateProjSpecific()
{
foreach (var target in ParentEvent.GetTargets(TargetTag))
{
SetHighlight(target);
}
}
private void SetHighlight(Entity entity)
{
if (entity is Item item)
{
int i = 0;
foreach (var itemContainer in item.GetComponents<Items.Components.ItemContainer>())
{
if (ItemContainerIndex == -1 || i == ItemContainerIndex)
{
SetHighlight(itemContainer.Inventory);
}
i++;
}
}
else if (entity is Character c)
{
SetHighlight(c.Inventory);
}
}
private void SetHighlight(Inventory inventory)
{
if (inventory?.visualSlots == null) { return; }
for (int i = 0; i < inventory.visualSlots.Length; i++)
{
if (inventory.visualSlots[i].HighlightTimer > 0) { continue; }
Item item = inventory.GetItemAt(i);
if (IsSuitableItem(item) ||
(Recursive && item?.OwnInventory != null && item.OwnInventory.FindAllItems(it => IsSuitableItem(it), recursive: true).Any()))
{
inventory.visualSlots[i].ShowBorderHighlight(highlightColor, 0.5f, 0.5f, 0.1f);
}
}
}
private bool IsSuitableItem(Item item)
{
return (ItemIdentifier.IsEmpty && item == null) ||
(item != null && (item.Prefab.Identifier == ItemIdentifier || item.HasTag(ItemIdentifier)));
}
}
@@ -8,20 +8,24 @@ partial class MessageBoxAction : EventAction
{ {
partial void UpdateProjSpecific() partial void UpdateProjSpecific()
{ {
if (Type == ActionType.Create) if (Type == ActionType.Create || Type == ActionType.ConnectObjective)
{ {
CreateMessageBox(); CreateMessageBox();
if (!ObjectiveTag.IsEmpty && GameMain.GameSession?.GameMode is TutorialMode tutorialMode) if (!ObjectiveTag.IsEmpty && GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
{ {
Identifier id = Identifier.IsEmpty ? Text : Identifier; Identifier id = Identifier.IfEmpty(Text);
var segment = Tutorial.Segment.CreateMessageBoxSegment(id, ObjectiveTag, CreateMessageBox); var segment = Tutorial.Segment.CreateMessageBoxSegment(id, ObjectiveTag, CreateMessageBox);
tutorialMode.Tutorial?.TriggerTutorialSegment(segment); tutorialMode.Tutorial?.TriggerTutorialSegment(segment, connectObjective: Type == ActionType.ConnectObjective);
} }
} }
else if (Type == ActionType.Close) else if (Type == ActionType.Close)
{ {
GUIMessageBox.Close(Tag); GUIMessageBox.Close(Tag);
} }
else if (Type == ActionType.Clear)
{
GUIMessageBox.CloseAll();
}
} }
public void CreateMessageBox() public void CreateMessageBox()
@@ -4,7 +4,7 @@ namespace Barotrauma;
partial class TutorialHighlightAction : EventAction partial class TutorialHighlightAction : EventAction
{ {
private static readonly Color highlightColor = Color.OrangeRed; private static readonly Color highlightColor = Color.Orange;
partial void UpdateProjSpecific() partial void UpdateProjSpecific()
{ {
@@ -19,32 +19,32 @@ partial class TutorialHighlightAction : EventAction
{ {
if (entity is Item i) if (entity is Item i)
{ {
SetHighlight(i); SetItemHighlight(i);
} }
else if (entity is Structure s) else if (entity is Structure s)
{ {
SetHighlight(s); SetStructureHighlight(s);
} }
else if (entity is Character c) else if (entity is Character c)
{ {
SetHighlight(c); SetCharacterHighlight(c);
} }
} }
private void SetHighlight(Item item) private void SetItemHighlight(Item item)
{ {
if (item.ExternalHighlight == State) { return; } if (item.ExternalHighlight == State) { return; }
item.SpriteColor = (State) ? highlightColor : Color.White; item.HighlightColor = State ? highlightColor : null;
item.ExternalHighlight = State; item.ExternalHighlight = State;
} }
private void SetHighlight(Structure structure) private void SetStructureHighlight(Structure structure)
{ {
structure.SpriteColor = (State) ? highlightColor : Color.White; structure.SpriteColor = State ? highlightColor : Color.White;
structure.ExternalHighlight = State; structure.ExternalHighlight = State;
} }
private void SetHighlight(Character character) private void SetCharacterHighlight(Character character)
{ {
character.ExternalHighlight = State; character.ExternalHighlight = State;
} }
@@ -11,13 +11,13 @@ partial class TutorialSegmentAction : EventAction
// Only need to create the segment when it's being triggered (otherwise the tutorial already has the segment instance) // Only need to create the segment when it's being triggered (otherwise the tutorial already has the segment instance)
if (Type == SegmentActionType.Trigger) if (Type == SegmentActionType.Trigger)
{ {
segment = Tutorial.Segment.CreateInfoBoxSegment(Id, ObjectiveTag, AutoPlayVideo ? Tutorials.AutoPlayVideo.Yes : Tutorials.AutoPlayVideo.No, segment = Tutorial.Segment.CreateInfoBoxSegment(Identifier, ObjectiveTag, AutoPlayVideo ? Tutorials.AutoPlayVideo.Yes : Tutorials.AutoPlayVideo.No,
new Tutorial.Segment.Text(TextTag, Width, Height, Anchor.Center), new Tutorial.Segment.Text(TextTag, Width, Height, Anchor.Center),
new Tutorial.Segment.Video(VideoFile, TextTag, Width, Height)); new Tutorial.Segment.Video(VideoFile, TextTag, Width, Height));
} }
else if (Type == SegmentActionType.Add) else if (Type == SegmentActionType.Add)
{ {
segment = Tutorial.Segment.CreateObjectiveSegment(Id, !ObjectiveTag.IsEmpty ? ObjectiveTag : Id); segment = Tutorial.Segment.CreateObjectiveSegment(Identifier, !ObjectiveTag.IsEmpty ? ObjectiveTag : Identifier);
} }
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode) if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
{ {
@@ -30,21 +30,21 @@ partial class TutorialSegmentAction : EventAction
tutorial.TriggerTutorialSegment(segment); tutorial.TriggerTutorialSegment(segment);
break; break;
case SegmentActionType.Complete: case SegmentActionType.Complete:
tutorial.CompleteTutorialSegment(Id); tutorial.CompleteTutorialSegment(Identifier);
break; break;
case SegmentActionType.Remove: case SegmentActionType.Remove:
tutorial.RemoveTutorialSegment(Id); tutorial.RemoveTutorialSegment(Identifier);
break; break;
case SegmentActionType.CompleteAndRemove: case SegmentActionType.CompleteAndRemove:
tutorial.CompleteTutorialSegment(Id); tutorial.CompleteTutorialSegment(Identifier);
tutorial.RemoveTutorialSegment(Id); tutorial.RemoveTutorialSegment(Identifier);
break; break;
} }
} }
} }
else else
{ {
DebugConsole.ShowError($"Error in event \"{ParentEvent.Prefab.Identifier}\": attempting to use TutorialSegmentAction during a non-Tutorial game mode!"); DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\": attempting to use TutorialSegmentAction during a non-Tutorial game mode!");
} }
} }
} }
@@ -0,0 +1,46 @@
using Microsoft.Xna.Framework;
using System.Linq;
namespace Barotrauma;
partial class UIHighlightAction : EventAction
{
private static readonly Color highlightColor = Color.Orange;
partial void UpdateProjSpecific()
{
bool useCircularFlash = false;
GUIComponent component = null;
if (Id != ElementId.None)
{
component = GUI.GetAdditions().FirstOrDefault(c => Equals(Id, c.UserData));
}
else if (!EntityIdentifier.IsEmpty)
{
component = GUI.GetAdditions().FirstOrDefault(c =>
c.UserData is MapEntityPrefab mep && mep.Identifier == EntityIdentifier || c.UserData is MapEntity me && me.Prefab.Identifier == EntityIdentifier);
}
else if (!OrderIdentifier.IsEmpty)
{
useCircularFlash = true;
if (!OrderTargetTag.IsEmpty)
{
component =
GUI.GetAdditions().FirstOrDefault(c =>
c.UserData is CrewManager.MinimapNodeData nodeData && nodeData.Order is Order order &&
order.Identifier == OrderIdentifier && order.Option == OrderOption && order.TargetEntity is Item item && item.HasTag(OrderTargetTag));
}
component ??=
GUI.GetAdditions().FirstOrDefault(c => c.UserData is Order order && order.Identifier == OrderIdentifier && order.Option == OrderOption) ??
GUI.GetAdditions().FirstOrDefault(c => c.UserData is Order order && order.Identifier == OrderIdentifier) ??
GUI.GetAdditions().FirstOrDefault(c => Equals(OrderCategory, c.UserData));
}
if (component != null && component.FlashTimer <= 0.0f)
{
component.Flash(highlightColor, useCircularFlash: useCircularFlash);
component.Bounce |= Bounce;
}
}
}
@@ -651,11 +651,11 @@ namespace Barotrauma
break; break;
case NetworkEventType.MISSION: case NetworkEventType.MISSION:
Identifier missionIdentifier = msg.ReadIdentifier(); Identifier missionIdentifier = msg.ReadIdentifier();
string missionName = msg.ReadString();
MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier); MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier);
if (prefab != null) if (prefab != null)
{ {
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", prefab.Name), new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", missionName),
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128)) Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
{ {
IconColor = prefab.IconColor IconColor = prefab.IconColor
@@ -410,7 +410,7 @@ namespace Barotrauma
{ {
y += yStep; y += yStep;
DrawString(spriteBatch, new Vector2(10, y), DrawString(spriteBatch, new Vector2(10, y),
"Sub pos: " + Submarine.MainSub.Position.ToPoint(), "Sub pos: " + Submarine.MainSub.WorldPosition.ToPoint(),
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont); Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
} }
@@ -879,6 +879,11 @@ namespace Barotrauma
} }
} }
} }
public static IEnumerable<GUIComponent> GetAdditions()
{
return additions;
}
#endregion #endregion
public static GUIComponent MouseOn { get; private set; } public static GUIComponent MouseOn { get; private set; }
@@ -486,7 +486,7 @@ namespace Barotrauma
{ {
if (bounceTimer > 3.0f || bounceDown) if (bounceTimer > 3.0f || bounceDown)
{ {
RectTransform.ScreenSpaceOffset = new Point(RectTransform.ScreenSpaceOffset.X, (int) -(bounceJump * 10f)); RectTransform.ScreenSpaceOffset = new Point(RectTransform.ScreenSpaceOffset.X, (int) -(bounceJump * 15f * GUI.Scale));
if (!bounceDown) if (!bounceDown)
{ {
bounceJump += deltaTime * 4; bounceJump += deltaTime * 4;
@@ -503,6 +503,7 @@ namespace Barotrauma
bounceJump = 0.0f; bounceJump = 0.0f;
bounceTimer = 0.0f; bounceTimer = 0.0f;
bounceDown = false; bounceDown = false;
Bounce = false;
} }
} }
} }
@@ -85,11 +85,11 @@ namespace Barotrauma
get { return sprite; } get { return sprite; }
set set
{ {
if (sprite == value) return; if (sprite == value) { return; }
sprite = value; sprite = value;
sourceRect = value == null ? Rectangle.Empty : value.SourceRect; sourceRect = value == null ? Rectangle.Empty : value.SourceRect;
origin = value == null ? Vector2.Zero : value.size / 2; origin = value == null ? Vector2.Zero : value.size / 2;
if (scaleToFit) RecalculateScale(); if (scaleToFit) { RecalculateScale(); }
} }
} }
@@ -601,16 +601,13 @@ namespace Barotrauma
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
float t = 0.0f; float t = 0.0f;
float startScroll = BarScroll * BarSize; float startScroll = BarScroll;
float distanceToTravel = ScrollBar.MaxValue - startScroll; float distanceToTravel = ScrollBar.MaxValue - startScroll;
float progress = startScroll;
float speed = distanceToTravel / duration; float speed = distanceToTravel / duration;
while (t < duration && !MathUtils.NearlyEqual(ScrollBar.MaxValue, BarScroll))
while (t < duration && !MathUtils.NearlyEqual(ScrollBar.MaxValue, progress))
{ {
t += CoroutineManager.DeltaTime; t += CoroutineManager.DeltaTime;
progress += speed * CoroutineManager.DeltaTime; BarScroll += speed * CoroutineManager.DeltaTime;
BarScroll = progress;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
@@ -207,7 +207,7 @@ namespace Barotrauma
{ {
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight); InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
CanBeFocused = false; CanBeFocused = false;
AutoClose = true; AutoClose = type == Type.InGame;
GUIStyle.Apply(InnerFrame, "", this); GUIStyle.Apply(InnerFrame, "", this);
var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), InnerFrame.RectTransform, Anchor.Center), var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), InnerFrame.RectTransform, Anchor.Center),
@@ -160,8 +160,13 @@ namespace Barotrauma
public static Point ItemFrameOffset => new Point(0, 3).Multiply(GUI.SlicedSpriteScale); public static Point ItemFrameOffset => new Point(0, 3).Multiply(GUI.SlicedSpriteScale);
public static GUIComponentStyle GetComponentStyle(string name) public static GUIComponentStyle GetComponentStyle(string styleName)
=> ComponentStyles.TryGet(name, out var style) ? style : null; {
return GetComponentStyle(styleName.ToIdentifier());
}
public static GUIComponentStyle GetComponentStyle(Identifier identifier)
=> ComponentStyles.TryGet(identifier, out var style) ? style : null;
public static void Apply(GUIComponent targetComponent, string styleName = "", GUIComponent parent = null) public static void Apply(GUIComponent targetComponent, string styleName = "", GUIComponent parent = null)
{ {
@@ -238,7 +238,7 @@ namespace Barotrauma
errorId = "Store.SelectStore:StoreDoesntExist"; errorId = "Store.SelectStore:StoreDoesntExist";
msg = $"Error selecting store with identifier \"{identifier}\" at {CurrentLocation}: store with the identifier doesn't exist at the location."; msg = $"Error selecting store with identifier \"{identifier}\" at {CurrentLocation}: store with the identifier doesn't exist at the location.";
} }
DebugConsole.ShowError(msg); DebugConsole.LogError(msg);
GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg); GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg);
} }
} }
@@ -263,7 +263,7 @@ namespace Barotrauma
} }
if (!msg.IsNullOrEmpty()) if (!msg.IsNullOrEmpty())
{ {
DebugConsole.ShowError(msg); DebugConsole.LogError(msg);
GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg); GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg);
} }
} }
@@ -1250,7 +1250,7 @@ namespace Barotrauma
} }
catch (NotImplementedException e) catch (NotImplementedException e)
{ {
DebugConsole.ShowError($"Error getting item price: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}"); DebugConsole.LogError($"Error getting item price: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
} }
} }
@@ -1808,7 +1808,7 @@ namespace Barotrauma
{ {
string errorMsg = $"Error creating a store quantity label text: unknown store tab.\n{e.StackTrace.CleanupStackTrace()}"; string errorMsg = $"Error creating a store quantity label text: unknown store tab.\n{e.StackTrace.CleanupStackTrace()}";
#if DEBUG #if DEBUG
DebugConsole.ShowError(errorMsg); DebugConsole.LogError(errorMsg);
#else #else
DebugConsole.AddWarning(errorMsg); DebugConsole.AddWarning(errorMsg);
#endif #endif
@@ -1882,7 +1882,7 @@ namespace Barotrauma
} }
catch (NotImplementedException e) catch (NotImplementedException e)
{ {
DebugConsole.ShowError($"Error getting item availability: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}"); DebugConsole.LogError($"Error getting item availability: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
} }
if (list != null && list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item) if (list != null && list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
{ {
@@ -1962,7 +1962,7 @@ namespace Barotrauma
} }
catch (NotImplementedException e) catch (NotImplementedException e)
{ {
DebugConsole.ShowError($"Error adding an item to the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}"); DebugConsole.LogError($"Error adding an item to the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false; return false;
} }
} }
@@ -1982,7 +1982,7 @@ namespace Barotrauma
} }
catch (NotImplementedException e) catch (NotImplementedException e)
{ {
DebugConsole.ShowError($"Error clearing the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}"); DebugConsole.LogError($"Error clearing the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false; return false;
} }
} }
@@ -2029,7 +2029,7 @@ namespace Barotrauma
} }
catch (NotImplementedException e) catch (NotImplementedException e)
{ {
DebugConsole.ShowError($"Error confirming the store transaction: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}"); DebugConsole.LogError($"Error confirming the store transaction: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false; return false;
} }
var itemsToRemove = new List<PurchasedItem>(); var itemsToRemove = new List<PurchasedItem>();
@@ -206,9 +206,9 @@ namespace Barotrauma
transferMenuButton.RectTransform.AbsoluteOffset = new Point(0, -pos - transferMenu.Rect.Height); transferMenuButton.RectTransform.AbsoluteOffset = new Point(0, -pos - transferMenu.Rect.Height);
} }
GameSession.UpdateTalentNotificationIndicator(talentPointNotification); GameSession.UpdateTalentNotificationIndicator(talentPointNotification);
if (Character.Controlled is { } controlled && talentResetButton != null && talentApplyButton != null) if (Character.Controlled?.Info is { } characterInfo && talentResetButton != null && talentApplyButton != null)
{ {
int talentCount = selectedTalents.Count - controlled.Info.GetUnlockedTalentsInTree().Count(); int talentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0; talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f) if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
{ {
@@ -1918,14 +1918,18 @@ namespace Barotrauma
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null); skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
CreateSkillList(controlledCharacter, info, skillListBox); CreateSkillList(controlledCharacter, info, skillListBox);
if (controlledCharacter != null) new GUIFrame(new RectTransform(new Vector2(1f, 1f), contentLayout.RectTransform), style: "HorizontalLine");
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.6f), contentLayout.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
if (controlledCharacter == null)
{
talentTreeListBox.Enabled = false;
}
else
{ {
if (!TalentTree.JobTalentTrees.TryGet(info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; } if (!TalentTree.JobTalentTrees.TryGet(info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
new GUIFrame(new RectTransform(new Vector2(1f, 1f), contentLayout.RectTransform), style: "HorizontalLine");
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.6f), contentLayout.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
selectedTalents = info.GetUnlockedTalentsInTree().ToList(); selectedTalents = info.GetUnlockedTalentsInTree().ToList();
List<GUITextBlock> subTreeNames = new List<GUITextBlock>(); List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
@@ -874,7 +874,7 @@ namespace Barotrauma
itemPrefab.SwappableItem.CanBeBought && itemPrefab.SwappableItem.CanBeBought &&
itemPrefab.SwappableItem.SwapIdentifier.Equals(item.Prefab.SwappableItem.SwapIdentifier, StringComparison.OrdinalIgnoreCase)).Cast<ItemPrefab>(); itemPrefab.SwappableItem.SwapIdentifier.Equals(item.Prefab.SwappableItem.SwapIdentifier, StringComparison.OrdinalIgnoreCase)).Cast<ItemPrefab>();
var linkedItems = Campaign.UpgradeManager.GetLinkedItemsToSwap(item) ?? new List<Item>() { item }; var linkedItems = UpgradeManager.GetLinkedItemsToSwap(item) ?? new List<Item>() { item };
//create the swap entry only for one of the items (the one with the smallest ID) //create the swap entry only for one of the items (the one with the smallest ID)
if (linkedItems.Min(it => it.ID) < item.ID) { return; } if (linkedItems.Min(it => it.ID) < item.ID) { return; }
@@ -910,7 +910,7 @@ namespace Barotrauma
GUILayoutGroup buttonLayout = new GUILayoutGroup(rectT(1f, 1f, toggleButton.Frame), isHorizontal: true); GUILayoutGroup buttonLayout = new GUILayoutGroup(rectT(1f, 1f, toggleButton.Frame), isHorizontal: true);
LocalizedString slotText = ""; LocalizedString slotText = "";
if (linkedItems.Count > 1) if (linkedItems.Count() > 1)
{ {
slotText = TextManager.GetWithVariable("weaponslot", "[number]", string.Join(", ", linkedItems.Select(it => (swappableEntities.IndexOf(it) + 1).ToString()))); slotText = TextManager.GetWithVariable("weaponslot", "[number]", string.Join(", ", linkedItems.Select(it => (swappableEntities.IndexOf(it) + 1).ToString())));
} }
@@ -982,7 +982,7 @@ namespace Barotrauma
bool isPurchased = item.AvailableSwaps.Contains(replacement); bool isPurchased = item.AvailableSwaps.Contains(replacement);
int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count; int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count();
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description, frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
price, replacement, price, replacement,
@@ -998,7 +998,7 @@ namespace Barotrauma
{ {
LocalizedString promptBody = TextManager.GetWithVariables(isPurchased ? "upgrades.itemswappromptbody" : "upgrades.purchaseitemswappromptbody", LocalizedString promptBody = TextManager.GetWithVariables(isPurchased ? "upgrades.itemswappromptbody" : "upgrades.purchaseitemswappromptbody",
("[itemtoinstall]", replacement.Name), ("[itemtoinstall]", replacement.Name),
("[amount]", (replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation) * linkedItems.Count).ToString())); ("[amount]", (replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation) * linkedItems.Count()).ToString()));
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () => currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
{ {
if (GameMain.NetworkMember != null) if (GameMain.NetworkMember != null)
@@ -1042,7 +1042,7 @@ namespace Barotrauma
} }
if (toggleButton.Selected) if (toggleButton.Selected)
{ {
var linkedItems = Campaign.UpgradeManager.GetLinkedItemsToSwap(item); var linkedItems = UpgradeManager.GetLinkedItemsToSwap(item);
foreach (var itemPreview in itemPreviews) foreach (var itemPreview in itemPreviews)
{ {
itemPreview.Value.OutlineColor = itemPreview.Value.Color = linkedItems.Contains(itemPreview.Key) ? GUIStyle.Orange : previewWhite; itemPreview.Value.OutlineColor = itemPreview.Value.Color = linkedItems.Contains(itemPreview.Key) ? GUIStyle.Orange : previewWhite;
@@ -1391,7 +1391,7 @@ namespace Barotrauma
{ {
if (selectedUpgradeCategoryLayout != null) if (selectedUpgradeCategoryLayout != null)
{ {
var linkedItems = HoveredEntity is Item hoveredItem ? Campaign.UpgradeManager.GetLinkedItemsToSwap(hoveredItem) : new List<Item>(); var linkedItems = HoveredEntity is Item hoveredItem ? UpgradeManager.GetLinkedItemsToSwap(hoveredItem) : new List<Item>();
if (selectedUpgradeCategoryLayout.FindChild(c => c.UserData is Item item && (item == HoveredEntity || linkedItems.Contains(item)), recursive: true) is GUIButton itemElement) if (selectedUpgradeCategoryLayout.FindChild(c => c.UserData is Item item && (item == HoveredEntity || linkedItems.Contains(item)), recursive: true) is GUIButton itemElement)
{ {
if (!itemElement.Selected) { itemElement.OnClicked(itemElement, itemElement.UserData); } if (!itemElement.Selected) { itemElement.OnClicked(itemElement, itemElement.UserData); }
@@ -139,7 +139,7 @@ namespace Barotrauma
} }
catch (NotImplementedException e) catch (NotImplementedException e)
{ {
DebugConsole.ShowError($"Error selling items: uknown store tab type \"{sellingMode}\".\n{e.StackTrace.CleanupStackTrace()}"); DebugConsole.LogError($"Error selling items: uknown store tab type \"{sellingMode}\".\n{e.StackTrace.CleanupStackTrace()}");
return; return;
} }
bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null; bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
@@ -148,7 +148,7 @@ namespace Barotrauma
var sellValues = GetSellValuesAtCurrentLocation(storeIdentifier, itemsToSell.Select(i => i.ItemPrefab)); var sellValues = GetSellValuesAtCurrentLocation(storeIdentifier, itemsToSell.Select(i => i.ItemPrefab));
if (!(Location.GetStore(storeIdentifier) is { } store)) if (!(Location.GetStore(storeIdentifier) is { } store))
{ {
DebugConsole.ShowError($"Error selling items at {Location}: no store with identifier \"{storeIdentifier}\" exists.\n{Environment.StackTrace.CleanupStackTrace()}"); DebugConsole.LogError($"Error selling items at {Location}: no store with identifier \"{storeIdentifier}\" exists.\n{Environment.StackTrace.CleanupStackTrace()}");
return; return;
} }
var storeSpecificSoldItems = GetSoldItems(storeIdentifier, create: true); var storeSpecificSoldItems = GetSoldItems(storeIdentifier, create: true);
@@ -291,29 +291,13 @@ namespace Barotrauma
return crewArea.Rect; return crewArea.Rect;
} }
/// <summary>
/// Remove the character from the crew (and crew menus).
/// </summary>
/// <param name="character">The character to remove</param>
/// <param name="removeInfo">If the character info is also removed, the character will not be visible in the round summary.</param>
public void RemoveCharacter(Character character, bool removeInfo = false, bool resetCrewListIndex = true)
{
if (character == null)
{
DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
characters.Remove(character);
if (removeInfo) { characterInfos.Remove(character.Info); }
if (resetCrewListIndex) { ResetCrewListIndex(character); }
}
/// <summary> /// <summary>
/// Add character to the list without actually adding it to the crew /// Add character to the list without actually adding it to the crew
/// </summary> /// </summary>
public GUIComponent AddCharacterToCrewList(Character character) public GUIComponent AddCharacterToCrewList(Character character)
{ {
if (character == null) { return null; } if (character == null) { return null; }
if (crewList.Content.Children.Any(c => c.UserData as Character == character)) { return null; }
var background = new GUIFrame( var background = new GUIFrame(
new RectTransform(crewListEntrySize, parent: crewList.Content.RectTransform, anchor: Anchor.TopRight), new RectTransform(crewListEntrySize, parent: crewList.Content.RectTransform, anchor: Anchor.TopRight),
@@ -509,7 +493,15 @@ namespace Barotrauma
return background; return background;
} }
private void SetCharacterComponentTooltip(GUIComponent characterComponent) public void RemoveCharacterFromCrewList(Character character)
{
if (crewList?.Content.GetChildByUserData(character) is { } component)
{
crewList.RemoveChild(component);
}
}
private static void SetCharacterComponentTooltip(GUIComponent characterComponent)
{ {
if (!(characterComponent?.UserData is Character character)) { return; } if (!(characterComponent?.UserData is Character character)) { return; }
if (character.Info?.Job?.Prefab == null) { return; } if (character.Info?.Job?.Prefab == null) { return; }
@@ -2821,7 +2813,7 @@ namespace Barotrauma
return node; return node;
} }
private struct MinimapNodeData public struct MinimapNodeData
{ {
public Order Order; public Order Order;
} }
@@ -761,6 +761,7 @@ namespace Barotrauma
item.PendingItemSwap = null; item.PendingItemSwap = null;
} }
} }
campaign.CampaignUI?.UpgradeStore?.RequestRefresh();
} }
} }
@@ -78,7 +78,7 @@ namespace Barotrauma.Tutorials
public Action OnClickObjective; public Action OnClickObjective;
public readonly TutorialSegmentType SegmentType; public TutorialSegmentType SegmentType { get; private set; }
public static Segment CreateInfoBoxSegment(Identifier id, Identifier objectiveTextTag, AutoPlayVideo autoPlayVideo, Text textContent = default, Video videoContent = default) public static Segment CreateInfoBoxSegment(Identifier id, Identifier objectiveTextTag, AutoPlayVideo autoPlayVideo, Text textContent = default, Video videoContent = default)
{ {
@@ -119,6 +119,12 @@ namespace Barotrauma.Tutorials
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag)); ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
SegmentType = TutorialSegmentType.Objective; SegmentType = TutorialSegmentType.Objective;
} }
public void ConnectMessageBox(Segment messageBoxSegment)
{
SegmentType = TutorialSegmentType.MessageBox;
OnClickObjective = messageBoxSegment.OnClickObjective;
}
} }
private bool completed; private bool completed;
@@ -144,6 +150,7 @@ namespace Barotrauma.Tutorials
private readonly EventPrefab eventPrefab; private readonly EventPrefab eventPrefab;
private CoroutineHandle tutorialCoroutine; private CoroutineHandle tutorialCoroutine;
private CoroutineHandle completedCoroutine;
private Character character; private Character character;
@@ -154,7 +161,7 @@ namespace Barotrauma.Tutorials
private SubmarineInfo startOutpost = null; private SubmarineInfo startOutpost = null;
public readonly List<(Entity entity, string iconStyle)> Icons = new List<(Entity entity, string iconStyle)>(); public readonly List<(Entity entity, Identifier iconStyle)> Icons = new List<(Entity entity, Identifier iconStyle)>();
#endregion #endregion
@@ -331,13 +338,16 @@ namespace Barotrauma.Tutorials
{ {
CoroutineManager.StopCoroutines(tutorialCoroutine); CoroutineManager.StopCoroutines(tutorialCoroutine);
} }
GUI.PreventPauseMenuToggle = false; if (completedCoroutine == null && !CoroutineManager.IsCoroutineRunning(completedCoroutine))
{
GUI.PreventPauseMenuToggle = false;
}
ContentRunning = false; ContentRunning = false;
infoBox = null; infoBox = null;
} }
else if (Character.Controlled.IsDead) else
{ {
CoroutineManager.StartCoroutine(Dead()); character = Character.Controlled;
} }
} }
} }
@@ -385,7 +395,7 @@ namespace Barotrauma.Tutorials
if (eventPrefab == null) if (eventPrefab == null)
{ {
DebugConsole.ShowError($"No tutorial event defined for the tutorial (identifier: \"{TutorialPrefab?.Identifier.ToString() ?? "null"})\""); DebugConsole.LogError($"No tutorial event defined for the tutorial (identifier: \"{TutorialPrefab?.Identifier.ToString() ?? "null"})\"");
yield return CoroutineStatus.Failure; yield return CoroutineStatus.Failure;
} }
@@ -399,7 +409,7 @@ namespace Barotrauma.Tutorials
} }
else else
{ {
DebugConsole.ShowError($"Failed to create an instance for a tutorial event (identifier: \"{eventPrefab.Identifier}\""); DebugConsole.LogError($"Failed to create an instance for a tutorial event (identifier: \"{eventPrefab.Identifier}\"");
yield return CoroutineStatus.Failure; yield return CoroutineStatus.Failure;
} }
@@ -409,10 +419,12 @@ namespace Barotrauma.Tutorials
public void Complete() public void Complete()
{ {
GameAnalyticsManager.AddDesignEvent($"Tutorial:{Identifier}:Completed"); GameAnalyticsManager.AddDesignEvent($"Tutorial:{Identifier}:Completed");
CoroutineManager.StartCoroutine(TutorialCompleted()); completedCoroutine = CoroutineManager.StartCoroutine(TutorialCompleted());
IEnumerable<CoroutineStatus> TutorialCompleted() IEnumerable<CoroutineStatus> TutorialCompleted()
{ {
while (GUI.PauseMenuOpen) { yield return CoroutineStatus.Running; }
GUI.PreventPauseMenuToggle = true; GUI.PreventPauseMenuToggle = true;
Character.Controlled.ClearInputs(); Character.Controlled.ClearInputs();
Character.Controlled = null; Character.Controlled = null;
@@ -423,7 +435,7 @@ namespace Barotrauma.Tutorials
var endCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: FadeOutTime); var endCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: FadeOutTime);
Completed = true; Completed = true;
while (endCinematic.Running) { yield return null; } while (endCinematic.Running) { yield return CoroutineStatus.Running; }
Stop(); Stop();
GameMain.MainMenuScreen.ReturnToMainMenu(null, null); GameMain.MainMenuScreen.ReturnToMainMenu(null, null);
@@ -432,16 +444,18 @@ namespace Barotrauma.Tutorials
private bool Restart(GUIButton button, object obj) private bool Restart(GUIButton button, object obj)
{ {
GUI.PreventPauseMenuToggle = false; GUIMessageBox.MessageBoxes.Clear();
GameMain.MainMenuScreen.ReturnToMainMenu(button, obj);
Start();
return true; return true;
} }
public void TriggerTutorialSegment(Segment segment) public void TriggerTutorialSegment(Segment segment, bool connectObjective = false)
{ {
if (segment.SegmentType != TutorialSegmentType.InfoBox) if (segment.SegmentType != TutorialSegmentType.InfoBox)
{ {
ActiveObjectives.Add(segment); ActiveObjectives.Add(segment);
AddToObjectiveList(segment); AddToObjectiveList(segment, connectObjective);
return; return;
} }
@@ -488,11 +502,14 @@ namespace Barotrauma.Tutorials
} }
if (GUIStyle.GetComponentStyle("ObjectiveIndicatorCompleted") is GUIComponentStyle style) if (GUIStyle.GetComponentStyle("ObjectiveIndicatorCompleted") is GUIComponentStyle style)
{ {
//return if already completed
if (segment.ObjectiveStateIndicator.Style == style) { return; }
segment.ObjectiveStateIndicator.ApplyStyle(style); segment.ObjectiveStateIndicator.ApplyStyle(style);
} }
segment.ObjectiveStateIndicator.Parent.Flash(color: GUIStyle.Green, flashDuration: 0.35f, useRectangleFlash: true); segment.ObjectiveStateIndicator.Parent.Flash(color: GUIStyle.Green, flashDuration: 0.35f, useRectangleFlash: true);
segment.ObjectiveButton.OnClicked = null; segment.ObjectiveButton.OnClicked = null;
segment.ObjectiveButton.CanBeFocused = false; segment.ObjectiveButton.CanBeFocused = false;
GameAnalyticsManager.AddDesignEvent($"Tutorial:{Identifier}:{segmentId}:Completed");
} }
public void RemoveTutorialSegment(Identifier segmentId) public void RemoveTutorialSegment(Identifier segmentId)
@@ -568,8 +585,18 @@ namespace Barotrauma.Tutorials
/// <summary> /// <summary>
/// Adds the segment to the objective list /// Adds the segment to the objective list
/// </summary> /// </summary>
private void AddToObjectiveList(Segment segment) private void AddToObjectiveList(Segment segment, bool connectExisting = false)
{ {
if (connectExisting)
{
if (ActiveObjectives.Find(o => o.Id == segment.Id) is { } existingSegment)
{
existingSegment.ConnectMessageBox(segment);
SetButtonBehavior(existingSegment);
}
return;
}
var frameRt = new RectTransform(new Vector2(1.0f, 0.1f), objectiveGroup.RectTransform) var frameRt = new RectTransform(new Vector2(1.0f, 0.1f), objectiveGroup.RectTransform)
{ {
AbsoluteOffset = GetObjectiveHiddenPosition(), AbsoluteOffset = GetObjectiveHiddenPosition(),
@@ -595,15 +622,25 @@ namespace Barotrauma.Tutorials
frame.RectTransform.IsFixedSize = true; frame.RectTransform.IsFixedSize = true;
var indicatorRt = new RectTransform(new Point(objectiveGroup.AbsoluteSpacing), frame.RectTransform, isFixedSize: true); var indicatorRt = new RectTransform(new Point(objectiveGroup.AbsoluteSpacing), frame.RectTransform, isFixedSize: true);
segment.ObjectiveStateIndicator = new GUIImage(indicatorRt, "ObjectiveIndicatorIncomplete");; segment.ObjectiveStateIndicator = new GUIImage(indicatorRt, "ObjectiveIndicatorIncomplete");
SetTransparent(segment.LinkedTextBlock); SetTransparent(segment.LinkedTextBlock);
segment.ObjectiveButton = new GUIButton(new RectTransform(Vector2.One, segment.LinkedTextBlock.RectTransform, Anchor.TopLeft, Pivot.TopLeft), style: null) segment.ObjectiveButton = new GUIButton(new RectTransform(Vector2.One, segment.LinkedTextBlock.RectTransform, Anchor.TopLeft, Pivot.TopLeft), style: null)
{ {
CanBeFocused = segment.SegmentType != TutorialSegmentType.Objective, ToolTip = objectiveTextTranslated
ToolTip = objectiveTextTranslated, };
OnClicked = (GUIButton btn, object userdata) => SetButtonBehavior(segment);
SetTransparent(segment.ObjectiveButton);
frameRt.MoveOverTime(new Point(0, frameRt.AbsoluteOffset.Y), ObjectiveComponentAnimationTime, onDoneMoving: () => objectiveGroup?.Recalculate());
static void SetTransparent(GUIComponent component) => component.Color = component.HoverColor = component.PressedColor = component.SelectedColor = Color.Transparent;
void SetButtonBehavior(Segment segment)
{
segment.ObjectiveButton.CanBeFocused = segment.SegmentType != TutorialSegmentType.Objective;
segment.ObjectiveButton.OnClicked = (GUIButton btn, object userdata) =>
{ {
if (segment.SegmentType == TutorialSegmentType.InfoBox) if (segment.SegmentType == TutorialSegmentType.InfoBox)
{ {
@@ -621,13 +658,8 @@ namespace Barotrauma.Tutorials
segment.OnClickObjective?.Invoke(); segment.OnClickObjective?.Invoke();
} }
return true; return true;
} };
}; }
SetTransparent(segment.ObjectiveButton);
frameRt.MoveOverTime(new Point(0, frameRt.AbsoluteOffset.Y), ObjectiveComponentAnimationTime, onDoneMoving: () => objectiveGroup?.Recalculate());
static void SetTransparent(GUIComponent component) => component.Color = component.HoverColor = component.PressedColor = component.SelectedColor = Color.Transparent;
} }
private void ReplaySegmentVideo(Segment segment) private void ReplaySegmentVideo(Segment segment)
@@ -4,6 +4,7 @@ using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -177,7 +178,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{ {
Color color = item.SpriteColor; Color color = item.GetSpriteColor(withHighlight: true);
if (brokenSprite == null) if (brokenSprite == null)
{ {
//broken doors turn black if no broken sprite has been configured //broken doors turn black if no broken sprite has been configured
@@ -220,11 +221,15 @@ namespace Barotrauma.Items.Components
color, 0.0f, doorSprite.Origin, item.Scale, item.SpriteEffects, doorSprite.Depth); color, 0.0f, doorSprite.Origin, item.Scale, item.SpriteEffects, doorSprite.Depth);
} }
if (brokenSprite != null && item.Health < item.MaxCondition) float maxCondition = item.Repairables.Any() ?
item.Repairables.Min(r => r.RepairThreshold) / 100.0f * item.MaxCondition :
item.MaxCondition;
float healthRatio = item.Health / maxCondition;
if (brokenSprite != null && healthRatio < 1.0f)
{ {
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - item.Health / item.MaxCondition) : Vector2.One; Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - healthRatio) : Vector2.One;
if (IsHorizontal) { scale.X = 1; } else { scale.Y = 1; } if (IsHorizontal) { scale.X = 1; } else { scale.Y = 1; }
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.MaxCondition : 1.0f; float alpha = fadeBrokenSprite ? 1.0f - healthRatio : 1.0f;
spriteBatch.Draw(brokenSprite.Texture, pos, spriteBatch.Draw(brokenSprite.Texture, pos,
getSourceRect(brokenSprite, openState, IsHorizontal), getSourceRect(brokenSprite, openState, IsHorizontal),
color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, item.SpriteEffects, color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, item.SpriteEffects,
@@ -580,7 +580,7 @@ namespace Barotrauma.Items.Components
GameMain.Instance.ResolutionChanged += OnResolutionChangedPrivate; GameMain.Instance.ResolutionChanged += OnResolutionChangedPrivate;
} }
protected void TryCreateDragHandle() protected virtual void TryCreateDragHandle()
{ {
if (GuiFrame != null && GuiFrameSource.GetAttributeBool("draggable", true)) if (GuiFrame != null && GuiFrameSource.GetAttributeBool("draggable", true))
{ {
@@ -593,6 +593,7 @@ namespace Barotrauma.Items.Components
int iconHeight = GUIStyle.ItemFrameMargin.Y / 4; int iconHeight = GUIStyle.ItemFrameMargin.Y / 4;
var dragIcon = new GUIImage(new RectTransform(new Point(GuiFrame.Rect.Width, iconHeight), handle.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, iconHeight / 2) }, var dragIcon = new GUIImage(new RectTransform(new Point(GuiFrame.Rect.Width, iconHeight), handle.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, iconHeight / 2) },
style: "GUIDragIndicatorHorizontal"); style: "GUIDragIndicatorHorizontal");
dragIcon.RectTransform.MinSize = new Point(0, iconHeight);
handle.ValidatePosition = (RectTransform rectT) => handle.ValidatePosition = (RectTransform rectT) =>
{ {
@@ -622,7 +623,7 @@ namespace Barotrauma.Items.Components
}; };
int buttonHeight = (int)(GUIStyle.ItemFrameMargin.Y * 0.4f); int buttonHeight = (int)(GUIStyle.ItemFrameMargin.Y * 0.4f);
new GUIButton(new RectTransform(new Point(buttonHeight), handle.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(buttonHeight / 4) }, new GUIButton(new RectTransform(new Point(buttonHeight), handle.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(buttonHeight / 4), MinSize = new Point(buttonHeight) },
style: "GUIButtonSettings") style: "GUIButtonSettings")
{ {
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
@@ -324,6 +324,8 @@ namespace Barotrauma.Items.Components
int i = 0; int i = 0;
foreach (Item containedItem in Inventory.AllItems) foreach (Item containedItem in Inventory.AllItems)
{ {
if (containedItem?.Sprite == null) { continue; }
if (AutoInteractWithContained) if (AutoInteractWithContained)
{ {
containedItem.IsHighlighted = item.IsHighlighted; containedItem.IsHighlighted = item.IsHighlighted;
@@ -344,7 +346,7 @@ namespace Barotrauma.Items.Components
containedItem.Sprite.Draw( containedItem.Sprite.Draw(
spriteBatch, spriteBatch,
new Vector2(currentItemPos.X, -currentItemPos.Y), new Vector2(currentItemPos.X, -currentItemPos.Y),
isWiringMode ? containedItem.GetSpriteColor() * 0.15f : containedItem.GetSpriteColor(), isWiringMode ? containedItem.GetSpriteColor(withHighlight: true) * 0.15f : containedItem.GetSpriteColor(withHighlight: true),
origin, origin,
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation ), -(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation ),
containedItem.Scale, containedItem.Scale,
@@ -96,6 +96,7 @@ namespace Barotrauma.Items.Components
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 0.8f), inputArea.RectTransform), childAnchor: Anchor.CenterLeft); var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 0.8f), inputArea.RectTransform), childAnchor: Anchor.CenterLeft);
activateButton = new GUIButton(new RectTransform(new Vector2(0.95f, 0.8f), buttonContainer.RectTransform), TextManager.Get("DeconstructorDeconstruct"), style: "DeviceButton") activateButton = new GUIButton(new RectTransform(new Vector2(0.95f, 0.8f), buttonContainer.RectTransform), TextManager.Get("DeconstructorDeconstruct"), style: "DeviceButton")
{ {
UserData = UIHighlightAction.ElementId.DeconstructButton,
TextBlock = { AutoScaleHorizontal = true }, TextBlock = { AutoScaleHorizontal = true },
OnClicked = OnActivateButtonClicked OnClicked = OnActivateButtonClicked
}; };
@@ -432,17 +433,22 @@ namespace Barotrauma.Items.Components
private bool OnActivateButtonClicked(GUIButton button, object obj) private bool OnActivateButtonClicked(GUIButton button, object obj)
{ {
var disallowedItem = inputContainer.Inventory.FindItem(i => !i.AllowDeconstruct, recursive: false); if (!IsActive)
if (disallowedItem != null && !DeconstructItemsSimultaneously)
{ {
int index = inputContainer.Inventory.FindIndex(disallowedItem); //don't allow turning on if there's non-deconstructable items in the queue
if (index >= 0 && index < inputContainer.Inventory.visualSlots.Length) var disallowedItem = inputContainer.Inventory.FindItem(i => !i.AllowDeconstruct, recursive: false);
if (disallowedItem != null && !DeconstructItemsSimultaneously)
{ {
var slot = inputContainer.Inventory.visualSlots[index]; int index = inputContainer.Inventory.FindIndex(disallowedItem);
slot?.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f); if (index >= 0 && index < inputContainer.Inventory.visualSlots.Length)
{
var slot = inputContainer.Inventory.visualSlots[index];
slot?.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f);
}
return true;
} }
return true;
} }
if (GameMain.Client != null) if (GameMain.Client != null)
{ {
pendingState = !IsActive; pendingState = !IsActive;
@@ -32,6 +32,8 @@ namespace Barotrauma.Items.Components
} }
private FabricationRecipe selectedItem; private FabricationRecipe selectedItem;
public Identifier SelectedItemIdentifier => SelectedItem?.TargetItem.Identifier ?? Identifier.Empty;
private GUIComponent inSufficientPowerWarning; private GUIComponent inSufficientPowerWarning;
private FabricationRecipe pendingFabricatedItem; private FabricationRecipe pendingFabricatedItem;
@@ -1013,12 +1013,14 @@ namespace Barotrauma.Items.Components
} }
else if (hullData.LinkedHulls.Any()) else if (hullData.LinkedHulls.Any())
{ {
hullData.HullWaterAmount = 0.0f; float waterVolume = 0.0f;
float totalVolume = 0.0f;
foreach (Hull linkedHull in hullData.LinkedHulls) foreach (Hull linkedHull in hullData.LinkedHulls)
{ {
hullData.HullWaterAmount += WaterDetector.GetWaterPercentage(linkedHull); waterVolume += linkedHull.WaterVolume;
totalVolume += linkedHull.Volume;
} }
hullData.HullWaterAmount /= hullData.LinkedHulls.Count; hullData.HullWaterAmount = MathHelper.Clamp((int)Math.Ceiling(waterVolume / totalVolume * 100), 0, 100);
} }
else else
{ {
@@ -1,10 +1,8 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Barotrauma.Particles; using Barotrauma.Particles;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -58,6 +56,7 @@ namespace Barotrauma.Items.Components
RelativeOffset = new Vector2(0, 0.1f) RelativeOffset = new Vector2(0, 0.1f)
}, style: "PowerButton") }, style: "PowerButton")
{ {
UserData = UIHighlightAction.ElementId.PowerButton,
OnClicked = (button, data) => OnClicked = (button, data) =>
{ {
TargetLevel = null; TargetLevel = null;
@@ -114,6 +113,7 @@ namespace Barotrauma.Items.Components
return true; return true;
} }
}; };
pumpSpeedSlider.Frame.UserData = UIHighlightAction.ElementId.PumpSpeedSlider;
var textsArea = new GUIFrame(new RectTransform(new Vector2(1, 0.25f), sliderArea.RectTransform, Anchor.BottomCenter), style: null); var textsArea = new GUIFrame(new RectTransform(new Vector2(1, 0.25f), sliderArea.RectTransform, Anchor.BottomCenter), style: null);
var outLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), textsArea.RectTransform, Anchor.CenterLeft), TextManager.Get("PumpOut"), var outLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), textsArea.RectTransform, Anchor.CenterLeft), TextManager.Get("PumpOut"),
textColor: GUIStyle.TextColorNormal, textAlignment: Alignment.CenterLeft, wrap: false, font: GUIStyle.SubHeadingFont); textColor: GUIStyle.TextColorNormal, textAlignment: Alignment.CenterLeft, wrap: false, font: GUIStyle.SubHeadingFont);
@@ -234,6 +234,7 @@ namespace Barotrauma.Items.Components
return false; return false;
} }
}; };
FissionRateScrollBar.Frame.UserData = UIHighlightAction.ElementId.FissionRateSlider;
TurbineOutputScrollBar = new GUIScrollBar(new RectTransform(sliderSize, rightArea.RectTransform, Anchor.TopCenter) TurbineOutputScrollBar = new GUIScrollBar(new RectTransform(sliderSize, rightArea.RectTransform, Anchor.TopCenter)
{ {
@@ -252,6 +253,7 @@ namespace Barotrauma.Items.Components
return false; return false;
} }
}; };
TurbineOutputScrollBar.Frame.UserData = UIHighlightAction.ElementId.TurbineOutputSlider;
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.2f), columnLeft.RectTransform)) var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.2f), columnLeft.RectTransform))
{ {
@@ -305,6 +307,7 @@ namespace Barotrauma.Items.Components
AutoTempSwitch = new GUIButton(new RectTransform(new Vector2(0.15f, 0.9f), topRightArea.RectTransform), AutoTempSwitch = new GUIButton(new RectTransform(new Vector2(0.15f, 0.9f), topRightArea.RectTransform),
style: "SwitchVertical") style: "SwitchVertical")
{ {
UserData = UIHighlightAction.ElementId.AutoTempSwitch,
Enabled = false, Enabled = false,
Selected = AutoTemp, Selected = AutoTemp,
ClickSound = GUISoundType.UISwitch, ClickSound = GUISoundType.UISwitch,
@@ -347,6 +350,7 @@ namespace Barotrauma.Items.Components
RelativeOffset = new Vector2(0, 0.1f) RelativeOffset = new Vector2(0, 0.1f)
}, style: "PowerButton") }, style: "PowerButton")
{ {
UserData = UIHighlightAction.ElementId.PowerButton,
OnClicked = (button, data) => OnClicked = (button, data) =>
{ {
PowerOn = !PowerOn; PowerOn = !PowerOn;
@@ -216,6 +216,7 @@ namespace Barotrauma.Items.Components
var sonarModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f + extraHeight), paddedControlContainer.RectTransform, Anchor.TopCenter), style: null); var sonarModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f + extraHeight), paddedControlContainer.RectTransform, Anchor.TopCenter), style: null);
SonarModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), sonarModeArea.RectTransform), string.Empty, style: "SwitchVertical") SonarModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), sonarModeArea.RectTransform), string.Empty, style: "SwitchVertical")
{ {
UserData = UIHighlightAction.ElementId.SonarModeSwitch,
Selected = false, Selected = false,
Enabled = true, Enabled = true,
ClickSound = GUISoundType.UISwitch, ClickSound = GUISoundType.UISwitch,
@@ -238,6 +239,7 @@ namespace Barotrauma.Items.Components
passiveTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), sonarModeRightSide.RectTransform, Anchor.TopLeft), passiveTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), sonarModeRightSide.RectTransform, Anchor.TopLeft),
TextManager.Get("SonarPassive"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRedSmall") TextManager.Get("SonarPassive"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRedSmall")
{ {
UserData = UIHighlightAction.ElementId.PassiveSonarIndicator,
ToolTip = TextManager.Get("SonarTipPassive"), ToolTip = TextManager.Get("SonarTipPassive"),
Selected = true, Selected = true,
Enabled = false Enabled = false
@@ -245,6 +247,7 @@ namespace Barotrauma.Items.Components
activeTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), sonarModeRightSide.RectTransform, Anchor.BottomLeft), activeTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), sonarModeRightSide.RectTransform, Anchor.BottomLeft),
TextManager.Get("SonarActive"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRedSmall") TextManager.Get("SonarActive"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRedSmall")
{ {
UserData = UIHighlightAction.ElementId.ActiveSonarIndicator,
ToolTip = TextManager.Get("SonarTipActive"), ToolTip = TextManager.Get("SonarTipActive"),
Selected = false, Selected = false,
Enabled = false Enabled = false
@@ -279,9 +282,14 @@ namespace Barotrauma.Items.Components
}; };
new GUIFrame(new RectTransform(new Vector2(0.8f, 0.01f), paddedControlContainer.RectTransform, Anchor.Center), style: "HorizontalLine") new GUIFrame(new RectTransform(new Vector2(0.8f, 0.01f), paddedControlContainer.RectTransform, Anchor.Center), style: "HorizontalLine")
{ UserData = "horizontalline" }; {
UserData = "horizontalline"
};
var directionalModeFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null); var directionalModeFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null)
{
UserData = UIHighlightAction.ElementId.DirectionalSonarFrame
};
directionalModeSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), directionalModeFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal") directionalModeSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), directionalModeFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal")
{ {
OnClicked = (button, data) => OnClicked = (button, data) =>
@@ -334,6 +342,18 @@ namespace Barotrauma.Items.Components
sonarView.RectTransform.RelativeOffset = new Vector2(0.13f * GUI.RelativeHorizontalAspectRatio, 0); sonarView.RectTransform.RelativeOffset = new Vector2(0.13f * GUI.RelativeHorizontalAspectRatio, 0);
sonarView.RectTransform.SetPosition(Anchor.BottomRight); sonarView.RectTransform.SetPosition(Anchor.BottomRight);
} }
var handle = GuiFrame.GetChild<GUIDragHandle>();
if (handle != null)
{
handle.RectTransform.Parent = controlContainer.RectTransform;
handle.RectTransform.Resize(Vector2.One);
handle.RectTransform.SetAsFirstChild();
}
}
protected override void TryCreateDragHandle()
{
base.TryCreateDragHandle();
} }
private void SetPingDirection(Vector2 direction) private void SetPingDirection(Vector2 direction)
@@ -1,13 +1,11 @@
using Barotrauma.Networking; using Barotrauma.Extensions;
using Barotrauma.Networking;
using FarseerPhysics; using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Linq; using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -141,6 +139,7 @@ namespace Barotrauma.Items.Components
var steeringModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), paddedControlContainer.RectTransform, Anchor.TopLeft), style: null); var steeringModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), paddedControlContainer.RectTransform, Anchor.TopLeft), style: null);
steeringModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), steeringModeArea.RectTransform), string.Empty, style: "SwitchVertical") steeringModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), steeringModeArea.RectTransform), string.Empty, style: "SwitchVertical")
{ {
UserData = UIHighlightAction.ElementId.SteeringModeSwitch,
Selected = autoPilot, Selected = autoPilot,
Enabled = true, Enabled = true,
ClickSound = GUISoundType.UISwitch, ClickSound = GUISoundType.UISwitch,
@@ -182,6 +181,7 @@ namespace Barotrauma.Items.Components
maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.TopCenter), maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.TopCenter),
TextManager.Get("SteeringMaintainPos"), font: GUIStyle.SmallFont, style: "GUIRadioButton") TextManager.Get("SteeringMaintainPos"), font: GUIStyle.SmallFont, style: "GUIRadioButton")
{ {
UserData = UIHighlightAction.ElementId.MaintainPosTickBox,
Enabled = autoPilot, Enabled = autoPilot,
Selected = maintainPos, Selected = maintainPos,
OnSelected = tickBox => OnSelected = tickBox =>
@@ -83,6 +83,7 @@ namespace Barotrauma.Items.Components
} }
}; };
rechargeSpeedSlider.Bar.RectTransform.MaxSize = new Point(rechargeSpeedSlider.Bar.Rect.Height); rechargeSpeedSlider.Bar.RectTransform.MaxSize = new Point(rechargeSpeedSlider.Bar.Rect.Height);
rechargeSpeedSlider.Frame.UserData = UIHighlightAction.ElementId.RechargeSpeedSlider;
// lower area -------------------------- // lower area --------------------------
@@ -165,6 +165,7 @@ namespace Barotrauma.Items.Components
repairingText = TextManager.Get("Repairing"); repairingText = TextManager.Get("Repairing");
RepairButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), progressBarHolder.RectTransform, Anchor.TopCenter), repairButtonText) RepairButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), progressBarHolder.RectTransform, Anchor.TopCenter), repairButtonText)
{ {
UserData = UIHighlightAction.ElementId.RepairButton,
OnClicked = (btn, obj) => OnClicked = (btn, obj) =>
{ {
requestStartFixAction = FixActions.Repair; requestStartFixAction = FixActions.Repair;
@@ -135,7 +135,7 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
DebugConsole.ShowError($"Error creating a CustomInterface component: unexpected NumberType \"{(ciElement.NumberType.HasValue ? ciElement.NumberType.Value.ToString() : "none")}\""); DebugConsole.LogError($"Error creating a CustomInterface component: unexpected NumberType \"{(ciElement.NumberType.HasValue ? ciElement.NumberType.Value.ToString() : "none")}\"");
} }
if (numberInput != null) if (numberInput != null)
{ {
@@ -118,7 +118,7 @@ namespace Barotrauma
return GetDrawDepth(SpriteDepth + DrawDepthOffset, Sprite); return GetDrawDepth(SpriteDepth + DrawDepthOffset, Sprite);
} }
public Color GetSpriteColor() public Color GetSpriteColor(bool withHighlight = false)
{ {
Color color = spriteColor; Color color = spriteColor;
if (Prefab.UseContainedSpriteColor && ownInventory != null) if (Prefab.UseContainedSpriteColor && ownInventory != null)
@@ -129,6 +129,17 @@ namespace Barotrauma
break; break;
} }
} }
if (withHighlight)
{
if (IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen)
{
color = GUIStyle.Orange * Math.Max(GetSpriteColor().A / (float)byte.MaxValue, 0.1f);
}
else if (IsHighlighted && HighlightColor.HasValue)
{
color = Color.Lerp(color, HighlightColor.Value, (MathF.Sin((float)Timing.TotalTime * 3.0f) + 1.0f) / 2.0f);
}
}
return color; return color;
} }
@@ -281,9 +292,7 @@ namespace Barotrauma
else if (!ShowItems) { return; } else if (!ShowItems) { return; }
} }
Color color = IsIncludedInSelection && editing ? GUIStyle.Blue : IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUIStyle.Orange * Math.Max(GetSpriteColor().A / (float) byte.MaxValue, 0.1f) : GetSpriteColor(); Color color = IsIncludedInSelection && editing ? GUIStyle.Blue : GetSpriteColor(withHighlight: true);
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null; bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null;
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null; bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
@@ -229,6 +229,7 @@ namespace Barotrauma
Submarine = Submarine.MainSub Submarine = Submarine.MainSub
}; };
item.SetTransform(ConvertUnits.ToSimUnits(Submarine.MainSub == null ? item.Position : item.Position - Submarine.MainSub.Position), 0.0f); item.SetTransform(ConvertUnits.ToSimUnits(Submarine.MainSub == null ? item.Position : item.Position - Submarine.MainSub.Position), 0.0f);
item.GetComponent<Items.Components.Door>()?.RefreshLinkedGap();
item.FindHull(); item.FindHull();
item.Submarine = Submarine.MainSub; item.Submarine = Submarine.MainSub;
@@ -264,10 +264,6 @@ namespace Barotrauma
{ {
Vector2 velocity = flowForce; Vector2 velocity = flowForce;
if (!IsHorizontal) if (!IsHorizontal)
{
velocity.X = Rand.Range(-100.0f, 100.0f) * open;
}
else
{ {
velocity.X *= Rand.Range(1.0f, 3.0f); velocity.X *= Rand.Range(1.0f, 3.0f);
} }
@@ -2,7 +2,6 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
@@ -88,12 +87,13 @@ namespace Barotrauma.Networking
TextManager.Get("BanPermanent") : TextManager.GetWithVariable("BanExpires", "[time]", bannedPlayer.ExpirationTime.Value.ToString()), TextManager.Get("BanPermanent") : TextManager.GetWithVariable("BanExpires", "[time]", bannedPlayer.ExpirationTime.Value.ToString()),
font: GUIStyle.SmallFont); font: GUIStyle.SmallFont);
LocalizedString reason = TextManager.GetServerMessage(bannedPlayer.Reason).Fallback(bannedPlayer.Reason);
var reasonText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedPlayerFrame.RectTransform), var reasonText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedPlayerFrame.RectTransform),
TextManager.Get("BanReason") + " " + TextManager.Get("BanReason") + " " +
(string.IsNullOrEmpty(bannedPlayer.Reason) ? TextManager.Get("None") : bannedPlayer.Reason), (string.IsNullOrEmpty(bannedPlayer.Reason) ? TextManager.Get("None") : reason),
font: GUIStyle.SmallFont, wrap: true) font: GUIStyle.SmallFont, wrap: true)
{ {
ToolTip = bannedPlayer.Reason ToolTip = reason
}; };
paddedPlayerFrame.Recalculate(); paddedPlayerFrame.Recalculate();
@@ -31,12 +31,17 @@ namespace Barotrauma.Networking
public bool IsOwner; public bool IsOwner;
public bool AllowKicking;
public bool IsDownloading; public bool IsDownloading;
public float Karma; public float Karma;
public bool AllowKicking =>
!IsOwner &&
!HasPermission(ClientPermissions.Ban) &&
!HasPermission(ClientPermissions.Kick) &&
!HasPermission(ClientPermissions.Unban);
public void UpdateSoundPosition() public void UpdateSoundPosition()
{ {
if (VoipSound == null) { return; } if (VoipSound == null) { return; }
@@ -362,8 +362,7 @@ namespace Barotrauma.Networking
// When this is set to true, we are approved and ready to go // When this is set to true, we are approved and ready to go
canStart = false; canStart = false;
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 40); DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 200);
DateTime reqAuthTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, 200);
// Loop until we are approved // Loop until we are approved
LocalizedString connectingText = TextManager.Get("Connecting"); LocalizedString connectingText = TextManager.Get("Connecting");
@@ -489,7 +488,10 @@ namespace Barotrauma.Networking
} }
GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
DebugConsole.ThrowError("Error while reading a message from server.", e); DebugConsole.ThrowError("Error while reading a message from server.", e);
new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", ("[message]", e.Message), ("[targetsite]", e.TargetSite.ToString()))); new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", ("[message]", e.Message), ("[targetsite]", e.TargetSite.ToString())))
{
DisplayInLoadingScreens = true
};
Quit(); Quit();
GameMain.ServerListScreen.Select(); GameMain.ServerListScreen.Select();
return; return;
@@ -965,10 +967,10 @@ namespace Barotrauma.Networking
} }
AttemptReconnect(disconnectPacket); AttemptReconnect(disconnectPacket);
} }
else if (disconnectPacket.ShouldShowMessage) else
{ {
ReturnToPreviousMenu(null, null); ReturnToPreviousMenu(null, null);
var msgBox = new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage) new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage)
{ {
DisplayInLoadingScreens = true DisplayInLoadingScreens = true
}; };
@@ -1033,6 +1035,7 @@ namespace Barotrauma.Networking
if (ClientPeer != null) if (ClientPeer != null)
{ {
//restore the previous list of content packages so we can reconnect immediately without having to recheck that the packages match //restore the previous list of content packages so we can reconnect immediately without having to recheck that the packages match
ClientPeer.ContentPackageOrderReceived = true;
ClientPeer.ServerContentPackages = prevContentPackages; ClientPeer.ServerContentPackages = prevContentPackages;
} }
} }
@@ -1721,6 +1724,7 @@ namespace Barotrauma.Networking
string subName = inc.ReadString(); string subName = inc.ReadString();
string subHash = inc.ReadString(); string subHash = inc.ReadString();
byte subClass = inc.ReadByte(); byte subClass = inc.ReadByte();
bool isShuttle = inc.ReadBoolean();
bool requiredContentPackagesInstalled = inc.ReadBoolean(); bool requiredContentPackagesInstalled = inc.ReadBoolean();
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.StringRepresentation == subHash); var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.StringRepresentation == subHash);
@@ -1730,6 +1734,7 @@ namespace Barotrauma.Networking
{ {
SubmarineClass = (SubmarineClass)subClass SubmarineClass = (SubmarineClass)subClass
}; };
if (isShuttle) { matchingSub.AddTag(SubmarineTag.Shuttle); }
} }
matchingSub.RequiredContentPackagesInstalled = requiredContentPackagesInstalled; matchingSub.RequiredContentPackagesInstalled = requiredContentPackagesInstalled;
ServerSubmarines.Add(matchingSub); ServerSubmarines.Add(matchingSub);
@@ -1780,7 +1785,6 @@ namespace Barotrauma.Networking
AccountInfo = tc.AccountInfo, AccountInfo = tc.AccountInfo,
Muted = tc.Muted, Muted = tc.Muted,
InGame = tc.InGame, InGame = tc.InGame,
AllowKicking = tc.AllowKicking,
IsOwner = tc.IsOwner IsOwner = tc.IsOwner
}; };
otherClients.Add(existingClient); otherClients.Add(existingClient);
@@ -1795,7 +1799,6 @@ namespace Barotrauma.Networking
existingClient.Muted = tc.Muted; existingClient.Muted = tc.Muted;
existingClient.InGame = tc.InGame; existingClient.InGame = tc.InGame;
existingClient.IsOwner = tc.IsOwner; existingClient.IsOwner = tc.IsOwner;
existingClient.AllowKicking = tc.AllowKicking;
existingClient.IsDownloading = tc.IsDownloading; existingClient.IsDownloading = tc.IsDownloading;
GameMain.NetLobbyScreen.SetPlayerNameAndJobPreference(existingClient); GameMain.NetLobbyScreen.SetPlayerNameAndJobPreference(existingClient);
if (Screen.Selected != GameMain.NetLobbyScreen && tc.CharacterId > 0) if (Screen.Selected != GameMain.NetLobbyScreen && tc.CharacterId > 0)
@@ -2404,7 +2407,7 @@ namespace Barotrauma.Networking
var subElement = subListChildren.FirstOrDefault(c => var subElement = subListChildren.FirstOrDefault(c =>
((SubmarineInfo)c.UserData).Name == newSub.Name && ((SubmarineInfo)c.UserData).Name == newSub.Name &&
((SubmarineInfo)c.UserData).MD5Hash.StringRepresentation == newSub.MD5Hash.StringRepresentation); ((SubmarineInfo)c.UserData).MD5Hash.StringRepresentation == newSub.MD5Hash.StringRepresentation);
if (subElement == null) continue; if (subElement == null) { continue; }
Color newSubTextColor = new Color(subElement.GetChild<GUITextBlock>().TextColor, 1.0f); Color newSubTextColor = new Color(subElement.GetChild<GUITextBlock>().TextColor, 1.0f);
subElement.GetChild<GUITextBlock>().TextColor = newSubTextColor; subElement.GetChild<GUITextBlock>().TextColor = newSubTextColor;
@@ -2429,7 +2432,7 @@ namespace Barotrauma.Networking
if (GameMain.NetLobbyScreen.FailedSelectedShuttle.HasValue && if (GameMain.NetLobbyScreen.FailedSelectedShuttle.HasValue &&
GameMain.NetLobbyScreen.FailedSelectedShuttle.Value.Name == newSub.Name && GameMain.NetLobbyScreen.FailedSelectedShuttle.Value.Name == newSub.Name &&
GameMain.NetLobbyScreen.FailedSelectedShuttle.Value.Name == newSub.MD5Hash.StringRepresentation) GameMain.NetLobbyScreen.FailedSelectedShuttle.Value.Hash == newSub.MD5Hash.StringRepresentation)
{ {
GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.StringRepresentation, GameMain.NetLobbyScreen.ShuttleList.ListBox); GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.StringRepresentation, GameMain.NetLobbyScreen.ShuttleList.ListBox);
} }
@@ -2583,6 +2586,7 @@ namespace Barotrauma.Networking
} }
} }
ChildServerRelay.ShutDown(); ChildServerRelay.ShutDown();
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
characterInfo?.Remove(); characterInfo?.Remove();
@@ -49,7 +49,7 @@ namespace Barotrauma.Networking
protected abstract void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body); protected abstract void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body);
protected ConnectionInitialization initializationStep; protected ConnectionInitialization initializationStep;
public bool ContentPackageOrderReceived { get; protected set; } public bool ContentPackageOrderReceived { get; set; }
protected int passwordSalt; protected int passwordSalt;
protected Steamworks.AuthTicket? steamAuthTicket; protected Steamworks.AuthTicket? steamAuthTicket;
private GUIMessageBox? passwordMsgBox; private GUIMessageBox? passwordMsgBox;
@@ -36,6 +36,8 @@ namespace Barotrauma.Networking
public override void Start() public override void Start()
{ {
if (isActive) { return; }
ContentPackageOrderReceived = false; ContentPackageOrderReceived = false;
steamAuthTicket = SteamManager.GetAuthSessionTicket(); steamAuthTicket = SteamManager.GetAuthSessionTicket();
@@ -189,6 +191,7 @@ namespace Barotrauma.Networking
if (packet is { SteamId: var steamId, Data: var data }) if (packet is { SteamId: var steamId, Data: var data })
{ {
OnP2PData(steamId, data, data.Length); OnP2PData(steamId, data, data.Length);
if (!isActive) { return; }
receivedBytes += data.Length; receivedBytes += data.Length;
} }
} }
@@ -391,7 +391,7 @@ namespace Barotrauma.Networking
for (int i = remotePeers.Count - 1; i >= 0; i--) for (int i = remotePeers.Count - 1; i >= 0; i--)
{ {
DisconnectPeer(remotePeers[i], peerDisconnectPacket); DisconnectPeer(remotePeers[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
} }
Thread.Sleep(100); Thread.Sleep(100);
@@ -37,6 +37,7 @@ namespace Barotrauma.Networking
switch (serverInfo.Endpoint) switch (serverInfo.Endpoint)
{ {
case LidgrenEndpoint { NetEndpoint: { Address: var address } }: case LidgrenEndpoint { NetEndpoint: { Address: var address } }:
GetIPAddressPing(serverInfo, address, onPingDiscovered); GetIPAddressPing(serverInfo, address, onPingDiscovered);
break; break;
case SteamP2PEndpoint steamP2PEndpoint: case SteamP2PEndpoint steamP2PEndpoint:
@@ -132,24 +133,30 @@ namespace Barotrauma.Networking
private static void GetIPAddressPing(ServerInfo serverInfo, IPAddress address, Action<ServerInfo> onPingDiscovered) private static void GetIPAddressPing(ServerInfo serverInfo, IPAddress address, Action<ServerInfo> onPingDiscovered)
{ {
lock (activePings) if (IPAddress.IsLoopback(address))
{ {
if (activePings.ContainsKey(address)) { return; } serverInfo.Ping = Option<int>.Some(0);
activePings.Add(address, activePings.Any() ? activePings.Values.Max() + 1 : 0); onPingDiscovered(serverInfo);
} }
else
serverInfo.Ping = Option<int>.None(); {
lock (activePings)
TaskPool.Add($"PingServerAsync ({address})", PingServerAsync(address, 1000),
rtt =>
{ {
if (!rtt.TryGetResult(out serverInfo.Ping)) { serverInfo.Ping = Option<int>.None(); } if (activePings.ContainsKey(address)) { return; }
onPingDiscovered(serverInfo); activePings.Add(address, activePings.Any() ? activePings.Values.Max() + 1 : 0);
lock (activePings) }
serverInfo.Ping = Option<int>.None();
TaskPool.Add($"PingServerAsync ({address})", PingServerAsync(address, 1000),
rtt =>
{ {
activePings.Remove(address); if (!rtt.TryGetResult(out serverInfo.Ping)) { serverInfo.Ping = Option<int>.None(); }
} onPingDiscovered(serverInfo);
}); lock (activePings)
{
activePings.Remove(address);
}
});
}
} }
private static async Task<Option<int>> PingServerAsync(IPAddress ipAddress, int timeOut) private static async Task<Option<int>> PingServerAsync(IPAddress ipAddress, int timeOut)
@@ -70,7 +70,7 @@ namespace Barotrauma.Networking
[Serialize(PlayStyle.Casual, IsPropertySaveable.Yes)] [Serialize(PlayStyle.Casual, IsPropertySaveable.Yes)]
public PlayStyle PlayStyle { get; set; } public PlayStyle PlayStyle { get; set; }
public Version GameVersion { get; set; } = new Version(0,0,0,0); public Version GameVersion { get; set; } = new Version(0, 0, 0, 0);
public Option<int> Ping = Option<int>.None(); public Option<int> Ping = Option<int>.None();
@@ -200,7 +200,7 @@ namespace Barotrauma.Networking
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"), TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"),
GameVersion.ToString())) GameVersion == new Version(0, 0, 0, 0) ? TextManager.Get("Unknown") : GameVersion.ToString()))
{ {
CanBeFocused = false CanBeFocused = false
}; };
@@ -397,10 +397,10 @@ namespace Barotrauma.Networking
public void UpdateInfo(Func<string, string?> valueGetter) public void UpdateInfo(Func<string, string?> valueGetter)
{ {
ServerMessage = valueGetter("message") ?? ""; ServerMessage = valueGetter("message") ?? "";
GameVersion = Version.TryParse(valueGetter("version"), out var version) if (Version.TryParse(valueGetter("version"), out var version))
? version {
: GameMain.Version; GameVersion = version;
}
if (int.TryParse(valueGetter("playercount"), out int playerCount)) { PlayerCount = playerCount; } if (int.TryParse(valueGetter("playercount"), out int playerCount)) { PlayerCount = playerCount; }
if (int.TryParse(valueGetter("maxplayernum"), out int maxPlayers)) { MaxPlayers = maxPlayers; } if (int.TryParse(valueGetter("maxplayernum"), out int maxPlayers)) { MaxPlayers = maxPlayers; }
if (Enum.TryParse(valueGetter("modeselectionmode"), out SelectionMode modeSelectionMode)) { ModeSelectionMode = modeSelectionMode; } if (Enum.TryParse(valueGetter("modeselectionmode"), out SelectionMode modeSelectionMode)) { ModeSelectionMode = modeSelectionMode; }
@@ -346,7 +346,7 @@ namespace Barotrauma
}; };
var missionName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission?.Name ?? TextManager.Get("NoMission"), font: GUIStyle.SubHeadingFont, wrap: true); var missionName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission?.Name ?? TextManager.Get("NoMission"), font: GUIStyle.SubHeadingFont, wrap: true);
// missionName.RectTransform.MinSize = new Point(0, (int)(missionName.Rect.Height * 1.5f)); missionName.RectTransform.MinSize = new Point(0, GUI.IntScale(15));
if (mission != null) if (mission != null)
{ {
var tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.X, 0) }, label: string.Empty) var tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.X, 0) }, label: string.Empty)
@@ -67,7 +67,9 @@ namespace Barotrauma
public static readonly Queue<ulong> WorkshopItemsToUpdate = new Queue<ulong>(); public static readonly Queue<ulong> WorkshopItemsToUpdate = new Queue<ulong>();
private readonly GUIListBox tutorialList; private GUIImage tutorialBanner;
private GUITextBlock tutorialHeader, tutorialDescription;
private GUIListBox tutorialList;
#region Creation #region Creation
public MainMenuScreen(GameMain game) public MainMenuScreen(GameMain game)
@@ -429,20 +431,7 @@ namespace Barotrauma
//---------------------------------------------------------------------- //----------------------------------------------------------------------
menuTabs[Tab.Tutorials] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing }); menuTabs[Tab.Tutorials] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
CreateTutorialTab();
//PLACEHOLDER
tutorialList = new GUIListBox(
new RectTransform(new Vector2(0.95f, 0.85f), menuTabs[Tab.Tutorials].RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.1f) })
{
PlaySoundOnSelect = true,
};
tutorialList.OnSelected += (component, obj) =>
{
(obj as Tutorial)?.Start();
return true;
};
CreateTutorialButtons();
this.game = game; this.game = game;
@@ -459,24 +448,68 @@ namespace Barotrauma
creditsPlayer = new CreditsPlayer(new RectTransform(Vector2.One, creditsContainer.RectTransform), "Content/Texts/Credits.xml"); creditsPlayer = new CreditsPlayer(new RectTransform(Vector2.One, creditsContainer.RectTransform), "Content/Texts/Credits.xml");
} }
private void CreateTutorialButtons() private void CreateTutorialTab()
{ {
var tutorialInnerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[Tab.Tutorials].RectTransform, Anchor.Center), style: "InnerFrame");
var tutorialContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), tutorialInnerFrame.RectTransform, Anchor.Center), isHorizontal: true) { RelativeSpacing = 0.02f, Stretch = true };
tutorialList = new GUIListBox(new RectTransform(new Vector2(0.4f, 1.0f), tutorialContent.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = (component, obj) =>
{
SelectTutorial(obj as Tutorial);
return true;
}
};
var tutorialPreview = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 1.0f), tutorialContent.RectTransform)) { RelativeSpacing = 0.05f, Stretch = true };
var imageContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f), tutorialPreview.RectTransform), style: "InnerFrame");
tutorialBanner = new GUIImage(new RectTransform(Vector2.One, imageContainer.RectTransform), style: null, scaleToFit: true);
var infoContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.4f), tutorialPreview.RectTransform), style: "GUIFrameListBox");
var infoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), infoContainer.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter);
tutorialHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.75f), infoContent.RectTransform), string.Empty, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center);
var startButton = new GUIButton(new RectTransform(new Vector2(0.5f, 0.0f), infoContent.RectTransform, Anchor.BottomRight), text: TextManager.Get("startgamebutton"))
{
IgnoreLayoutGroups = true,
OnClicked = (component, obj) =>
{
(tutorialList.SelectedData as Tutorial)?.Start();
return true;
}
};
Tutorial firstTutorial = null;
foreach (var tutorialPrefab in TutorialPrefab.Prefabs.OrderBy(p => p.Order)) foreach (var tutorialPrefab in TutorialPrefab.Prefabs.OrderBy(p => p.Order))
{ {
var tutorial = new Tutorial(tutorialPrefab); var tutorial = new Tutorial(tutorialPrefab);
var tutorialText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), tutorialList.Content.RectTransform), tutorial.DisplayName, textAlignment: Alignment.Center, font: GUIStyle.LargeFont) firstTutorial ??= tutorial;
var tutorialText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), tutorialList.Content.RectTransform), tutorial.DisplayName)
{ {
TextColor = GUIStyle.Green, Padding = new Vector4(30.0f * GUI.Scale, 0,0,0),
UserData = tutorial UserData = tutorial
}; };
tutorialText.RectTransform.MinSize = new Point(0, (int)(tutorialText.TextSize.Y * 2));
} }
GUITextBlock.AutoScaleAndNormalize(tutorialList.Content.Children.Select(c => c as GUITextBlock));
tutorialList.Select(firstTutorial);
}
private void SelectTutorial(Tutorial tutorial)
{
tutorialHeader.Text = tutorial.DisplayName;
tutorial.TutorialPrefab.Banner?.EnsureLazyLoaded();
tutorialBanner.Sprite = tutorial.TutorialPrefab.Banner;
tutorialBanner.Color = tutorial.TutorialPrefab.Banner == null ? Color.Black : Color.White;
} }
public static void UpdateInstanceTutorialButtons() public static void UpdateInstanceTutorialButtons()
{ {
if (GameMain.MainMenuScreen is not MainMenuScreen menuScreen) { return; } if (GameMain.MainMenuScreen is not MainMenuScreen menuScreen) { return; }
menuScreen.tutorialList.ClearChildren(); menuScreen.tutorialList.ClearChildren();
menuScreen.CreateTutorialButtons(); menuScreen.CreateTutorialTab();
} }
#endregion #endregion
@@ -746,12 +779,13 @@ namespace Barotrauma
private void UpdateTutorialList() private void UpdateTutorialList()
{ {
foreach (GUITextBlock tutorialText in menuTabs[Tab.Tutorials].GetChild<GUIListBox>().Content.Children) foreach (GUITextBlock tutorialText in tutorialList.Content.Children)
{ {
var tutorial = (Tutorial)tutorialText.UserData; var tutorial = (Tutorial)tutorialText.UserData;
tutorialText.Text = CompletedTutorials.Instance.Contains(tutorial.Identifier) ? if (CompletedTutorials.Instance.Contains(tutorial.Identifier) && tutorialText.GetChild<GUIImage>() == null)
TextManager.GetWithVariable("tutorialcompleted", "[tutorialname]", tutorial.DisplayName) : {
tutorial.DisplayName; new GUIImage(new RectTransform(new Point((int)(tutorialText.Padding.X * 0.8f)), tutorialText.RectTransform, Anchor.CenterLeft), style: "ObjectiveIndicatorCompleted");
}
} }
} }
@@ -1862,7 +1862,7 @@ namespace Barotrauma
SubmarineType.Wreck => "Content/Map/Wrecks/{0}", SubmarineType.Wreck => "Content/Map/Wrecks/{0}",
SubmarineType.BeaconStation => "Content/Map/BeaconStations/{0}", SubmarineType.BeaconStation => "Content/Map/BeaconStations/{0}",
SubmarineType.EnemySubmarine => "Content/Map/EnemySubmarines/{0}", SubmarineType.EnemySubmarine => "Content/Map/EnemySubmarines/{0}",
SubmarineType.OutpostModule => "Content/Map/Outposts/{0}", SubmarineType.OutpostModule => MainSub.Info.FilePath.Contains("RuinModules") ? "Content/Map/RuinModules/{0}" : "Content/Map/Outposts/{0}",
_ => throw new InvalidOperationException() _ => throw new InvalidOperationException()
}, savePath); }, savePath);
modProject.ModVersion = ""; modProject.ModVersion = "";
@@ -195,13 +195,13 @@ namespace Barotrauma.Sounds
GainMultipliers[index] = gain; GainMultipliers[index] = gain;
} }
} }
private Dictionary<string, CategoryModifier> categoryModifiers;
private readonly Dictionary<string, CategoryModifier> categoryModifiers = new Dictionary<string, CategoryModifier>();
public SoundManager() public SoundManager()
{ {
loadedSounds = new List<Sound>(); loadedSounds = new List<Sound>();
streamingThread = null; streamingThread = null;
categoryModifiers = null;
sourcePools = new SoundSourcePool[2]; sourcePools = new SoundSourcePool[2];
playingChannels[(int)SourcePoolIndex.Default] = new SoundChannel[SOURCE_COUNT]; playingChannels[(int)SourcePoolIndex.Default] = new SoundChannel[SOURCE_COUNT];
@@ -235,7 +235,7 @@ namespace Barotrauma.Sounds
CompressionDynamicRangeGain = 1.0f; CompressionDynamicRangeGain = 1.0f;
} }
private void SetAudioOutputDevice(string deviceName) private static void SetAudioOutputDevice(string deviceName)
{ {
var config = GameSettings.CurrentConfig; var config = GameSettings.CurrentConfig;
config.Audio.AudioOutputDevice = deviceName; config.Audio.AudioOutputDevice = deviceName;
@@ -249,7 +249,7 @@ namespace Barotrauma.Sounds
DebugConsole.NewMessage($"Attempting to open ALC device \"{deviceName}\""); DebugConsole.NewMessage($"Attempting to open ALC device \"{deviceName}\"");
alcDevice = IntPtr.Zero; alcDevice = IntPtr.Zero;
int alcError = Al.NoError; int alcError;
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
alcDevice = Alc.OpenDevice(deviceName); alcDevice = Alc.OpenDevice(deviceName);
@@ -371,8 +371,10 @@ namespace Barotrauma.Sounds
throw new System.IO.FileNotFoundException($"Sound file \"{filePath}\" doesn't exist! Content package \"{(element.ContentPackage?.Name ?? "Unknown")}\"."); throw new System.IO.FileNotFoundException($"Sound file \"{filePath}\" doesn't exist! Content package \"{(element.ContentPackage?.Name ?? "Unknown")}\".");
} }
var newSound = new OggSound(this, filePath, stream, xElement: element); var newSound = new OggSound(this, filePath, stream, xElement: element)
newSound.BaseGain = element.GetAttributeFloat("volume", 1.0f); {
BaseGain = element.GetAttributeFloat("volume", 1.0f)
};
float range = element.GetAttributeFloat("range", 1000.0f); float range = element.GetAttributeFloat("range", 1000.0f);
newSound.BaseNear = range * 0.4f; newSound.BaseNear = range * 0.4f;
newSound.BaseFar = range; newSound.BaseFar = range;
@@ -537,14 +539,16 @@ namespace Barotrauma.Sounds
{ {
if (Disabled) { return; } if (Disabled) { return; }
category = category.ToLower(); category = category.ToLower();
if (categoryModifiers == null) categoryModifiers = new Dictionary<string, CategoryModifier>(); lock (categoryModifiers)
if (!categoryModifiers.ContainsKey(category))
{ {
categoryModifiers.Add(category, new CategoryModifier(index, gain, false)); if (!categoryModifiers.ContainsKey(category))
} {
else categoryModifiers.Add(category, new CategoryModifier(index, gain, false));
{ }
categoryModifiers[category].SetGainMultiplier(index, gain); else
{
categoryModifiers[category].SetGainMultiplier(index, gain);
}
} }
for (int i = 0; i < playingChannels.Length; i++) for (int i = 0; i < playingChannels.Length; i++)
@@ -562,23 +566,26 @@ namespace Barotrauma.Sounds
} }
} }
public float GetCategoryGainMultiplier(string category, int index=-1) public float GetCategoryGainMultiplier(string category, int index = -1)
{ {
if (Disabled) { return 0.0f; } if (Disabled) { return 0.0f; }
category = category.ToLower(); category = category.ToLower();
if (categoryModifiers == null || !categoryModifiers.ContainsKey(category)) return 1.0f; lock (categoryModifiers)
if (index < 0)
{ {
float accumulatedMultipliers = 1.0f; if (categoryModifiers == null || !categoryModifiers.TryGetValue(category, out CategoryModifier categoryModifier)) { return 1.0f; }
for (int i = 0; i < categoryModifiers[category].GainMultipliers.Length; i++) if (index < 0)
{ {
accumulatedMultipliers *= categoryModifiers[category].GainMultipliers[i]; float accumulatedMultipliers = 1.0f;
for (int i = 0; i < categoryModifier.GainMultipliers.Length; i++)
{
accumulatedMultipliers *= categoryModifier.GainMultipliers[i];
}
return accumulatedMultipliers;
}
else
{
return categoryModifier.GainMultipliers[index];
} }
return accumulatedMultipliers;
}
else
{
return categoryModifiers[category].GainMultipliers[index];
} }
} }
@@ -587,15 +594,16 @@ namespace Barotrauma.Sounds
if (Disabled) { return; } if (Disabled) { return; }
category = category.ToLower(); category = category.ToLower();
lock (categoryModifiers)
if (categoryModifiers == null) { categoryModifiers = new Dictionary<string, CategoryModifier>(); }
if (!categoryModifiers.ContainsKey(category))
{ {
categoryModifiers.Add(category, new CategoryModifier(0, 1.0f, muffle)); if (!categoryModifiers.ContainsKey(category))
} {
else categoryModifiers.Add(category, new CategoryModifier(0, 1.0f, muffle));
{ }
categoryModifiers[category].Muffle = muffle; else
{
categoryModifiers[category].Muffle = muffle;
}
} }
for (int i = 0; i < playingChannels.Length; i++) for (int i = 0; i < playingChannels.Length; i++)
@@ -618,8 +626,11 @@ namespace Barotrauma.Sounds
if (Disabled) { return false; } if (Disabled) { return false; }
category = category.ToLower(); category = category.ToLower();
if (categoryModifiers == null || !categoryModifiers.ContainsKey(category)) { return false; } lock (categoryModifiers)
return categoryModifiers[category].Muffle; {
if (categoryModifiers == null || !categoryModifiers.TryGetValue(category, out CategoryModifier categoryModifier)) { return false; }
return categoryModifier.Muffle;
}
} }
public void Update() public void Update()
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.19.5.0</Version> <Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.19.5.0</Version> <Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.19.5.0</Version> <Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.19.5.0</Version> <Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.19.5.0</Version> <Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -512,13 +512,10 @@ namespace Barotrauma
msg.WriteByte((byte)TeamID); msg.WriteByte((byte)TeamID);
break; break;
case AddToCrewEventData addToCrewEventData: case AddToCrewEventData addToCrewEventData:
msg.WriteByte((byte)addToCrewEventData.TeamType); // team id msg.WriteNetSerializableStruct(addToCrewEventData.ItemTeamChange);
ushort[] inventoryItemIDs = addToCrewEventData.InventoryItems.Select(item => item.ID).ToArray(); break;
msg.WriteUInt16((ushort)inventoryItemIDs.Length); case RemoveFromCrewEventData removeFromCrewEventData:
for (int i = 0; i < inventoryItemIDs.Length; i++) msg.WriteNetSerializableStruct(removeFromCrewEventData.ItemTeamChange);
{
msg.WriteUInt16(inventoryItemIDs[i]);
}
break; break;
case UpdateExperienceEventData _: case UpdateExperienceEventData _:
msg.WriteInt32(Info.ExperiencePoints); msg.WriteInt32(Info.ExperiencePoints);
@@ -1613,6 +1613,7 @@ namespace Barotrauma.Networking
outmsg.WriteString(sub.Name); outmsg.WriteString(sub.Name);
outmsg.WriteString(sub.MD5Hash.ToString()); outmsg.WriteString(sub.MD5Hash.ToString());
outmsg.WriteByte((byte)sub.SubmarineClass); outmsg.WriteByte((byte)sub.SubmarineClass);
outmsg.WriteBoolean(sub.HasTag(SubmarineTag.Shuttle));
outmsg.WriteBoolean(sub.RequiredContentPackagesInstalled); outmsg.WriteBoolean(sub.RequiredContentPackagesInstalled);
} }
@@ -1836,10 +1837,6 @@ namespace Barotrauma.Networking
InGame = client.InGame, InGame = client.InGame,
HasPermissions = client.Permissions != ClientPermissions.None, HasPermissions = client.Permissions != ClientPermissions.None,
IsOwner = client.Connection == OwnerConnection, IsOwner = client.Connection == OwnerConnection,
AllowKicking = client.Connection != OwnerConnection &&
!client.HasPermission(ClientPermissions.Ban) &&
!client.HasPermission(ClientPermissions.Kick) &&
!client.HasPermission(ClientPermissions.Unban),
IsDownloading = FileSender.ActiveTransfers.Any(t => t.Connection == client.Connection) IsDownloading = FileSender.ActiveTransfers.Any(t => t.Connection == client.Connection)
}; };
@@ -3383,7 +3380,7 @@ namespace Barotrauma.Networking
private IEnumerable<CoroutineStatus> SendClientPermissionsAfterClientListSynced(Client recipient, Client client) private IEnumerable<CoroutineStatus> SendClientPermissionsAfterClientListSynced(Client recipient, Client client)
{ {
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 10); DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 10);
while (recipient.LastRecvClientListUpdate < LastClientListUpdateID) while (NetIdUtils.IdMoreRecent(LastClientListUpdateID, recipient.LastRecvClientListUpdate))
{ {
if (DateTime.Now > timeOut || GameMain.Server == null || !connectedClients.Contains(recipient)) if (DateTime.Now > timeOut || GameMain.Server == null || !connectedClients.Contains(recipient))
{ {
@@ -225,7 +225,7 @@ namespace Barotrauma.Networking
} }
else if (!packetHeader.IsConnectionInitializationStep()) else if (!packetHeader.IsConnectionInitializationStep())
{ {
if (!(connectedClients.Find(c => c is LidgrenConnection l && l.NetConnection == lidgrenMsg.SenderConnection) is LidgrenConnection conn)) if (connectedClients.Find(c => c is LidgrenConnection l && l.NetConnection == lidgrenMsg.SenderConnection) is not LidgrenConnection conn)
{ {
if (pendingClient != null) if (pendingClient != null)
{ {
@@ -373,7 +373,7 @@ namespace Barotrauma.Networking
{ {
if (netServer == null) { return; } if (netServer == null) { return; }
if (!(conn is LidgrenConnection lidgrenConn)) { return; } if (conn is not LidgrenConnection lidgrenConn) { return; }
if (connectedClients.Contains(lidgrenConn)) if (connectedClients.Contains(lidgrenConn))
{ {
@@ -432,7 +432,7 @@ namespace Barotrauma.Networking
} }
else else
{ {
if (!packet.SteamId.TryUnwrap(out var id) || !(id is SteamId steamId)) if (!packet.SteamId.TryUnwrap(out var id) || id is not SteamId steamId)
{ {
if (requireSteamAuth) if (requireSteamAuth)
{ {
@@ -167,7 +167,14 @@ namespace Barotrauma.Networking
if (pendingClient.AccountInfo.AccountId.TryUnwrap(out var id)) { banAccountId(id); } if (pendingClient.AccountInfo.AccountId.TryUnwrap(out var id)) { banAccountId(id); }
pendingClient.AccountInfo.OtherMatchingIds.ForEach(banAccountId); pendingClient.AccountInfo.OtherMatchingIds.ForEach(banAccountId);
serverSettings.BanList.BanPlayer(pendingClient.Name ?? "Player", pendingClient.Connection.Endpoint, banReason, duration); if (pendingClient.AccountInfo.AccountId.TryUnwrap(out var accountId))
{
serverSettings.BanList.BanPlayer(pendingClient.Name ?? "Player", accountId, banReason, duration);
}
else
{
serverSettings.BanList.BanPlayer(pendingClient.Name ?? "Player", pendingClient.Connection.Endpoint, banReason, duration);
}
} }
protected bool IsPendingClientBanned(PendingClient pendingClient, out string? banReason) protected bool IsPendingClientBanned(PendingClient pendingClient, out string? banReason)
@@ -1,8 +1,6 @@
#nullable enable #nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
@@ -139,7 +137,27 @@ namespace Barotrauma.Networking
pendingClient?.Heartbeat(); pendingClient?.Heartbeat();
connectedClient?.Heartbeat(); connectedClient?.Heartbeat();
if (serverSettings.BanList.IsBanned(senderSteamId, out string banReason) || if (packetHeader.IsConnectionInitializationStep())
{
if (!initialization.HasValue) { return; }
ConnectionInitialization initializationStep = initialization.Value;
if (pendingClient != null)
{
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, sentOwnerSteamId));
ReadConnectionInitializationStep(
pendingClient,
new ReadWriteMessage(inc.Buffer, inc.BitPosition, inc.LengthBits, false),
initializationStep);
}
else if (initializationStep == ConnectionInitialization.ConnectionStarted)
{
pendingClient = new PendingClient(new SteamP2PConnection(senderSteamId));
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, sentOwnerSteamId));
pendingClients.Add(pendingClient);
}
}
else if (serverSettings.BanList.IsBanned(senderSteamId, out string banReason) ||
serverSettings.BanList.IsBanned(sentOwnerSteamId, out banReason)) serverSettings.BanList.IsBanned(sentOwnerSteamId, out banReason))
{ {
if (pendingClient != null) if (pendingClient != null)
@@ -167,24 +185,6 @@ namespace Barotrauma.Networking
//message exists solely as a heartbeat, ignore its contents //message exists solely as a heartbeat, ignore its contents
return; return;
} }
else if (packetHeader.IsConnectionInitializationStep())
{
if (!initialization.HasValue) { return; }
ConnectionInitialization initializationStep = initialization.Value;
if (pendingClient != null)
{
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, sentOwnerSteamId));
ReadConnectionInitializationStep(
pendingClient,
new ReadWriteMessage(inc.Buffer, inc.BitPosition, inc.LengthBits, false),
initializationStep);
}
else if (initializationStep == ConnectionInitialization.ConnectionStarted)
{
pendingClients.Add(new PendingClient(new SteamP2PConnection(senderSteamId)));
}
}
else if (connectedClient != null) else if (connectedClient != null)
{ {
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc); var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
@@ -248,7 +248,7 @@ namespace Barotrauma.Networking
{ {
if (!started) { return; } if (!started) { return; }
if (!(conn is SteamP2PConnection steamP2PConn)) { return; } if (conn is not SteamP2PConnection steamP2PConn) { return; }
if (!connectedClients.Contains(steamP2PConn) && conn != OwnerConnection) if (!connectedClients.Contains(steamP2PConn) && conn != OwnerConnection)
{ {
@@ -256,7 +256,7 @@ namespace Barotrauma.Networking
return; return;
} }
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || !(connAccountId is SteamId connSteamId)) { return; } if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || connAccountId is not SteamId) { return; }
byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _); byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
@@ -292,9 +292,9 @@ namespace Barotrauma.Networking
{ {
if (!started) { return; } if (!started) { return; }
if (!(conn is SteamP2PConnection steamp2pConn)) { return; } if (conn is not SteamP2PConnection steamp2pConn) { return; }
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || !(connAccountId is SteamId connSteamId)) { return; } if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || connAccountId is not SteamId connSteamId) { return; }
SendDisconnectMessage(connSteamId, peerDisconnectPacket); SendDisconnectMessage(connSteamId, peerDisconnectPacket);
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.19.5.0</Version> <Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -466,7 +466,12 @@ namespace Barotrauma
} }
} }
} }
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run && Character.CanRun));
//if someone is grabbing the bot and the bot isn't trying to run anywhere, let them keep dragging and "control" the bot
if (Character.SelectedBy == null || run)
{
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run && Character.CanRun));
}
bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f && (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X)); bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f && (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X));
if (steeringManager == insideSteering) if (steeringManager == insideSteering)
@@ -913,11 +913,14 @@ namespace Barotrauma
private void RemoveFollowTarget() private void RemoveFollowTarget()
{ {
if (arrestingRegistered) if (followTargetObjective != null)
{ {
followTargetObjective.Completed -= OnArrestTargetReached; if (arrestingRegistered)
{
followTargetObjective.Completed -= OnArrestTargetReached;
}
RemoveSubObjective(ref followTargetObjective);
} }
RemoveSubObjective(ref followTargetObjective);
arrestingRegistered = false; arrestingRegistered = false;
} }
@@ -595,6 +595,10 @@ namespace Barotrauma
{ {
return c.CurrentHull; return c.CurrentHull;
} }
else if (target is Structure structure)
{
return Hull.FindHull(structure.Position, useWorldCoordinates: false);
}
else if (target is Gap g) else if (target is Gap g)
{ {
return g.FlowTargetHull; return g.FlowTargetHull;
@@ -54,7 +54,7 @@ namespace Barotrauma
if (ValidContainableItemIdentifiers.None()) if (ValidContainableItemIdentifiers.None())
{ {
#if DEBUG #if DEBUG
DebugConsole.ShowError($"No valid containable item identifiers found for the Load Item objective targeting {Container}"); DebugConsole.LogError($"No valid containable item identifiers found for the Load Item objective targeting {Container}");
#endif #endif
Abandon = true; Abandon = true;
return; return;
@@ -250,7 +250,7 @@ namespace Barotrauma
catch (NotImplementedException) catch (NotImplementedException)
{ {
#if DEBUG #if DEBUG
DebugConsole.ShowError($"Unexpected target condition \"{TargetItemCondition}\" in local function GetConditionBasedProperty"); DebugConsole.LogError($"Unexpected target condition \"{TargetItemCondition}\" in local function GetConditionBasedProperty");
#endif #endif
return 0.0f; return 0.0f;
} }
@@ -90,7 +90,7 @@ namespace Barotrauma
catch (NotImplementedException) catch (NotImplementedException)
{ {
#if DEBUG #if DEBUG
DebugConsole.ShowError($"Unexpected target condition \"{targetCondition}\" in AIObjectiveLoadItems.ItemMatchesTargetCondition"); DebugConsole.LogError($"Unexpected target condition \"{targetCondition}\" in AIObjectiveLoadItems.ItemMatchesTargetCondition");
#endif #endif
return false; return false;
} }
@@ -37,6 +37,8 @@ namespace Barotrauma
public Func<bool> completionCondition; public Func<bool> completionCondition;
private bool isDoneOperating; private bool isDoneOperating;
public float? OverridePriority = null;
protected override float GetPriority() protected override float GetPriority()
{ {
bool isOrder = objectiveManager.IsOrder(this); bool isOrder = objectiveManager.IsOrder(this);
@@ -52,7 +54,11 @@ namespace Barotrauma
} }
else else
{ {
if (isOrder) if (OverridePriority.HasValue)
{
Priority = OverridePriority.Value;
}
else if (isOrder)
{ {
Priority = objectiveManager.GetOrderPriority(this); Priority = objectiveManager.GetOrderPriority(this);
} }
@@ -135,7 +141,7 @@ namespace Barotrauma
float value = CumulatedDevotion + (max * PriorityModifier); float value = CumulatedDevotion + (max * PriorityModifier);
Priority = MathHelper.Clamp(value, 0, max); Priority = MathHelper.Clamp(value, 0, max);
} }
else else if (!OverridePriority.HasValue)
{ {
float value = CumulatedDevotion + (AIObjectiveManager.LowestOrderPriority * PriorityModifier); float value = CumulatedDevotion + (AIObjectiveManager.LowestOrderPriority * PriorityModifier);
float max = AIObjectiveManager.LowestOrderPriority - 1; float max = AIObjectiveManager.LowestOrderPriority - 1;
@@ -204,8 +210,15 @@ namespace Barotrauma
{ {
if (!character.IsClimbing && character.CanInteractWith(target.Item, out _, checkLinked: false)) if (!character.IsClimbing && character.CanInteractWith(target.Item, out _, checkLinked: false))
{ {
HumanAIController.FaceTarget(target.Item); if (target.Item.GetComponent<Controller>() is not Controller { ControlCharacterPose: true })
if (character.SelectedItem != target.Item) {
HumanAIController.FaceTarget(target.Item);
}
else
{
HumanAIController.SteeringManager.Reset();
}
if (character.SelectedItem != target.Item && character.SelectedSecondaryItem != target.Item)
{ {
target.Item.TryInteract(character, forceSelectKey: true); target.Item.TryInteract(character, forceSelectKey: true);
} }
@@ -278,7 +278,7 @@ namespace Barotrauma
float cprSuitability = targetCharacter.Oxygen < 0.0f ? -targetCharacter.Oxygen * 100.0f : 0.0f; float cprSuitability = targetCharacter.Oxygen < 0.0f ? -targetCharacter.Oxygen * 100.0f : 0.0f;
//find which treatments are the most suitable to treat the character's current condition //find which treatments are the most suitable to treat the character's current condition
targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, normalize: false, predictFutureDuration: 10.0f); targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, user: character, normalize: false, predictFutureDuration: 10.0f);
//check if we already have a suitable treatment for any of the afflictions //check if we already have a suitable treatment for any of the afflictions
foreach (Affliction affliction in GetSortedAfflictions(targetCharacter)) foreach (Affliction affliction in GetSortedAfflictions(targetCharacter))
@@ -3,9 +3,8 @@ using Barotrauma.Items.Components;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -151,7 +150,7 @@ namespace Barotrauma
public OrderPrefab(ContentXElement orderElement, OrdersFile file) : base(file, orderElement.GetAttributeIdentifier("identifier", "")) public OrderPrefab(ContentXElement orderElement, OrdersFile file) : base(file, orderElement.GetAttributeIdentifier("identifier", ""))
{ {
Name = TextManager.Get($"OrderName.{Identifier}"); Name = TextManager.Get($"OrderName.{Identifier}");
ContextualName = TextManager.Get($"OrderNameContextual.{Identifier}"); ContextualName = TextManager.Get($"OrderNameContextual.{Identifier}").Fallback(Name);
string targetItemType = orderElement.GetAttributeString("targetitemtype", ""); string targetItemType = orderElement.GetAttributeString("targetitemtype", "");
if (!string.IsNullOrWhiteSpace(targetItemType)) if (!string.IsNullOrWhiteSpace(targetItemType))
@@ -435,7 +434,7 @@ namespace Barotrauma
} }
catch (NotImplementedException e) catch (NotImplementedException e)
{ {
DebugConsole.ShowError($"Error creating a new Order instance: unexpected target type \"{targetType}\".\n{e.StackTrace.CleanupStackTrace()}"); DebugConsole.LogError($"Error creating a new Order instance: unexpected target type \"{targetType}\".\n{e.StackTrace.CleanupStackTrace()}");
return null; return null;
} }
} }
@@ -336,7 +336,7 @@ namespace Barotrauma
{ {
ApplyTestPose(); ApplyTestPose();
} }
else else if (character.SelectedBy == null)
{ {
if (character.LockHands) if (character.LockHands)
{ {
@@ -356,7 +356,7 @@ namespace Barotrauma
HandIK(rightHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength); HandIK(rightHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
HandIK(leftHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength); HandIK(leftHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
} }
if (Anim != Animation.UsingItem && character.SelectedBy == null) if (Anim != Animation.UsingItem)
{ {
if (Anim != Animation.UsingItemWhileClimbing) if (Anim != Animation.UsingItemWhileClimbing)
{ {
@@ -1716,8 +1716,16 @@ namespace Barotrauma
} }
else if (target is AICharacter && target != Character.Controlled) else if (target is AICharacter && target != Character.Controlled)
{ {
target.AnimController.TargetDir = WorldPosition.X > target.WorldPosition.X ? Direction.Right : Direction.Left; if (target.AnimController.Dir > 0 == WorldPosition.X > target.WorldPosition.X)
Vector2 movement = (character.SimPosition + Vector2.UnitX * 0.5f * Dir) - target.SimPosition; {
target.AnimController.LockFlippingUntil = (float)Timing.TotalTime + 0.5f;
}
else
{
target.AnimController.TargetDir = WorldPosition.X > target.WorldPosition.X ? Direction.Right : Direction.Left;
}
//make the target stand 0.5 meters away from this character, on the side they're currently at
Vector2 movement = (character.SimPosition + Vector2.UnitX * 0.5f * Math.Sign(target.SimPosition.X - character.SimPosition.X)) - target.SimPosition;
target.AnimController.TargetMovement = movement.LengthSquared() > 0.01f ? movement : Vector2.Zero; target.AnimController.TargetMovement = movement.LengthSquared() > 0.01f ? movement : Vector2.Zero;
} }
} }
@@ -402,9 +402,8 @@ namespace Barotrauma
} }
else else
{ {
string afflictionIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant(); Identifier afflictionIdentifier = subElement.GetAttributeIdentifier("identifier", "");
afflictionPrefab = AfflictionPrefab.Prefabs[afflictionIdentifier]; if (!AfflictionPrefab.Prefabs.TryGet(afflictionIdentifier, out afflictionPrefab))
if (afflictionPrefab == null)
{ {
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionIdentifier + "\" not found."); DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionIdentifier + "\" not found.");
continue; continue;
@@ -430,15 +429,13 @@ namespace Barotrauma
Afflictions.Clear(); Afflictions.Clear();
foreach (var subElement in element.GetChildElements("affliction")) foreach (var subElement in element.GetChildElements("affliction"))
{ {
AfflictionPrefab afflictionPrefab;
Affliction affliction; Affliction affliction;
Identifier afflictionIdentifier = subElement.GetAttributeIdentifier("identifier", ""); Identifier afflictionIdentifier = subElement.GetAttributeIdentifier("identifier", "");
if (!AfflictionPrefab.Prefabs.ContainsKey(afflictionIdentifier)) if (!AfflictionPrefab.Prefabs.TryGet(afflictionIdentifier, out AfflictionPrefab afflictionPrefab))
{ {
DebugConsole.ThrowError($"Error in an Attack defined in \"{parentDebugName}\" - could not find an affliction with the identifier \"{afflictionIdentifier}\"."); DebugConsole.ThrowError($"Error in an Attack defined in \"{parentDebugName}\" - could not find an affliction with the identifier \"{afflictionIdentifier}\".");
continue; continue;
} }
afflictionPrefab = AfflictionPrefab.Prefabs[afflictionIdentifier];
affliction = afflictionPrefab.Instantiate(0.0f); affliction = afflictionPrefab.Instantiate(0.0f);
affliction.Deserialize(subElement); affliction.Deserialize(subElement);
//backwards compatibility //backwards compatibility
@@ -549,6 +549,10 @@ namespace Barotrauma
set set
{ {
lockHandsTimer = MathHelper.Clamp(lockHandsTimer + (value ? 1.0f : -0.5f), 0.0f, 10.0f); lockHandsTimer = MathHelper.Clamp(lockHandsTimer + (value ? 1.0f : -0.5f), 0.0f, 10.0f);
if (value)
{
SelectedCharacter = null;
}
#if CLIENT #if CLIENT
HintManager.OnHandcuffed(this); HintManager.OnHandcuffed(this);
#endif #endif
@@ -599,13 +603,10 @@ namespace Barotrauma
get { return selectedCharacter; } get { return selectedCharacter; }
set set
{ {
if (value == selectedCharacter) return; if (value == selectedCharacter) { return; }
if (selectedCharacter != null) if (selectedCharacter != null) { selectedCharacter.selectedBy = null; }
selectedCharacter.selectedBy = null;
selectedCharacter = value; selectedCharacter = value;
if (selectedCharacter != null) if (selectedCharacter != null) {selectedCharacter.selectedBy = this; }
selectedCharacter.selectedBy = this;
#if CLIENT #if CLIENT
CharacterHealth.SetHealthBarVisibility(value == null); CharacterHealth.SetHealthBarVisibility(value == null);
#endif #endif
@@ -707,6 +708,14 @@ namespace Barotrauma
get { return CurrentHull == null || CurrentHull.LethalPressure > 5.0f; } get { return CurrentHull == null || CurrentHull.LethalPressure > 5.0f; }
} }
/// <summary>
/// Can be used by status effects
/// </summary>
public AnimController.Animation Anim
{
get { return AnimController?.Anim ?? AnimController.Animation.None; }
}
public const float KnockbackCooldown = 5.0f; public const float KnockbackCooldown = 5.0f;
public float KnockbackCooldownTimer; public float KnockbackCooldownTimer;
@@ -2620,7 +2629,7 @@ namespace Barotrauma
if (!AllowInput) if (!AllowInput)
{ {
FocusedCharacter = null; FocusedCharacter = null;
if (SelectedCharacter != null) DeselectCharacter(); if (SelectedCharacter != null) { DeselectCharacter(); }
return; return;
} }
} }
@@ -3636,7 +3645,7 @@ namespace Barotrauma
string modifiedMessage = ChatMessage.ApplyDistanceEffect(message.Message, message.MessageType.Value, this, Controlled); string modifiedMessage = ChatMessage.ApplyDistanceEffect(message.Message, message.MessageType.Value, this, Controlled);
if (!string.IsNullOrEmpty(modifiedMessage)) if (!string.IsNullOrEmpty(modifiedMessage))
{ {
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(info.Name, modifiedMessage, message.MessageType.Value, this); GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(Name, modifiedMessage, message.MessageType.Value, this);
} }
} }
#endif #endif
@@ -4020,6 +4029,7 @@ namespace Barotrauma
if (newStun > 0.0f) if (newStun > 0.0f)
{ {
SelectedItem = SelectedSecondaryItem = null; SelectedItem = SelectedSecondaryItem = null;
if (SelectedCharacter != null) { DeselectCharacter(); }
} }
HealthUpdateInterval = 0.0f; HealthUpdateInterval = 0.0f;
} }
@@ -1,8 +1,9 @@
using System.Collections.Generic; using Barotrauma.Networking;
using System.Collections.Immutable;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -25,9 +26,10 @@ namespace Barotrauma
UpdateSkills = 12, UpdateSkills = 12,
UpdateMoney = 13, UpdateMoney = 13,
UpdatePermanentStats = 14, UpdatePermanentStats = 14,
RemoveFromCrew = 15,
MinValue = 0, MinValue = 0,
MaxValue = 14 MaxValue = 15
} }
private interface IEventData : NetEntityEvent.IData private interface IEventData : NetEntityEvent.IData
@@ -133,18 +135,30 @@ namespace Barotrauma
public EventType EventType => EventType.TeamChange; public EventType EventType => EventType.TeamChange;
} }
[NetworkSerialize]
public readonly record struct ItemTeamChange(CharacterTeamType TeamId, ImmutableArray<UInt16> ItemIds) : INetSerializableStruct;
public struct AddToCrewEventData : IEventData public struct AddToCrewEventData : IEventData
{ {
public EventType EventType => EventType.AddToCrew; public EventType EventType => EventType.AddToCrew;
public readonly CharacterTeamType TeamType; public readonly ItemTeamChange ItemTeamChange;
public readonly ImmutableArray<Item> InventoryItems;
public AddToCrewEventData(CharacterTeamType teamType, IEnumerable<Item> inventoryItems) public AddToCrewEventData(CharacterTeamType teamType, IEnumerable<Item> inventoryItems)
{ {
TeamType = teamType; ItemTeamChange = new ItemTeamChange(teamType, inventoryItems.Select(it => it.ID).ToImmutableArray());
InventoryItems = inventoryItems.ToImmutableArray();
} }
}
public struct RemoveFromCrewEventData : IEventData
{
public EventType EventType => EventType.RemoveFromCrew;
public readonly ItemTeamChange ItemTeamChange;
public RemoveFromCrewEventData(CharacterTeamType teamType, IEnumerable<Item> inventoryItems)
{
ItemTeamChange = new ItemTeamChange(teamType, inventoryItems.Select(it => it.ID).ToImmutableArray());
}
} }
public struct UpdateExperienceEventData : IEventData public struct UpdateExperienceEventData : IEventData
@@ -82,10 +82,10 @@ namespace Barotrauma
public UInt16 networkUpdateID; public UInt16 networkUpdateID;
} }
private List<NetInputMem> memInput = new List<NetInputMem>(); private readonly List<NetInputMem> memInput = new List<NetInputMem>();
private List<CharacterStateInfo> memState = new List<CharacterStateInfo>(); private readonly List<CharacterStateInfo> memState = new List<CharacterStateInfo>();
private List<CharacterStateInfo> memLocalState = new List<CharacterStateInfo>(); private readonly List<CharacterStateInfo> memLocalState = new List<CharacterStateInfo>();
public float healthUpdateTimer; public float healthUpdateTimer;
@@ -1038,7 +1038,7 @@ namespace Barotrauma
/// <param name="treatmentSuitability">A dictionary where the key is the identifier of the item and the value the suitability</param> /// <param name="treatmentSuitability">A dictionary where the key is the identifier of the item and the value the suitability</param>
/// <param name="normalize">If true, the suitability values are normalized between 0 and 1. If not, they're arbitrary values defined in the medical item XML, where negative values are unsuitable, and positive ones suitable.</param> /// <param name="normalize">If true, the suitability values are normalized between 0 and 1. If not, they're arbitrary values defined in the medical item XML, where negative values are unsuitable, and positive ones suitable.</param>
/// <param name="predictFutureDuration">If above 0, the method will take into account how much currently active status effects while affect the afflictions in the next x seconds.</param> /// <param name="predictFutureDuration">If above 0, the method will take into account how much currently active status effects while affect the afflictions in the next x seconds.</param>
public void GetSuitableTreatments(Dictionary<Identifier, float> treatmentSuitability, bool normalize, Limb limb = null, bool ignoreHiddenAfflictions = false, float predictFutureDuration = 0.0f) public void GetSuitableTreatments(Dictionary<Identifier, float> treatmentSuitability, bool normalize, Character user, Limb limb = null, bool ignoreHiddenAfflictions = false, float predictFutureDuration = 0.0f)
{ {
//key = item identifier //key = item identifier
//float = suitability //float = suitability
@@ -1062,7 +1062,18 @@ namespace Barotrauma
} }
if (strength <= affliction.Prefab.TreatmentThreshold) { continue; } if (strength <= affliction.Prefab.TreatmentThreshold) { continue; }
if (ignoreHiddenAfflictions && strength < affliction.Prefab.ShowIconThreshold) { continue; }
if (ignoreHiddenAfflictions)
{
if (user == Character)
{
if (strength < affliction.Prefab.ShowIconThreshold) { continue; }
}
else
{
if (strength < affliction.Prefab.ShowIconToOthersThreshold) { continue; }
}
}
foreach (KeyValuePair<Identifier, float> treatment in affliction.Prefab.TreatmentSuitability) foreach (KeyValuePair<Identifier, float> treatment in affliction.Prefab.TreatmentSuitability)
{ {
@@ -1153,10 +1164,10 @@ namespace Barotrauma
msg.WriteRangedSingle( msg.WriteRangedSingle(
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength), MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
0.0f, affliction.Prefab.MaxStrength, 8); 0.0f, affliction.Prefab.MaxStrength, 8);
msg.WriteByte((byte)affliction.Prefab.PeriodicEffects.Count()); msg.WriteByte((byte)affliction.Prefab.PeriodicEffects.Count);
foreach (AfflictionPrefab.PeriodicEffect periodicEffect in affliction.Prefab.PeriodicEffects) foreach (AfflictionPrefab.PeriodicEffect periodicEffect in affliction.Prefab.PeriodicEffects)
{ {
msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], periodicEffect.MinInterval, periodicEffect.MaxInterval, 8); msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], 0, periodicEffect.MaxInterval, 8);
} }
} }
@@ -1178,7 +1189,7 @@ namespace Barotrauma
msg.WriteRangedSingle( msg.WriteRangedSingle(
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength), MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
0.0f, affliction.Prefab.MaxStrength, 8); 0.0f, affliction.Prefab.MaxStrength, 8);
msg.WriteByte((byte)affliction.Prefab.PeriodicEffects.Count()); msg.WriteByte((byte)affliction.Prefab.PeriodicEffects.Count);
foreach (AfflictionPrefab.PeriodicEffect periodicEffect in affliction.Prefab.PeriodicEffects) foreach (AfflictionPrefab.PeriodicEffect periodicEffect in affliction.Prefab.PeriodicEffects)
{ {
msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], periodicEffect.MinInterval, periodicEffect.MaxInterval, 8); msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], periodicEffect.MinInterval, periodicEffect.MaxInterval, 8);
@@ -86,6 +86,28 @@ namespace Barotrauma
{ {
DebugConsole.ThrowError("Error in DamageModifier config (" + parentDebugName + ") - define afflictions using identifiers or types instead of names."); DebugConsole.ThrowError("Error in DamageModifier config (" + parentDebugName + ") - define afflictions using identifiers or types instead of names.");
} }
foreach (var afflictionType in parsedAfflictionTypes)
{
if (!AfflictionPrefab.Prefabs.Any(p => p.AfflictionType == afflictionType))
{
createWarningOrError($"Potentially invalid damage modifier in \"{parentDebugName}\". Could not find any afflictions of the type \"{afflictionType}\". Did you mean to use an affliction identifier instead?");
}
}
foreach (var afflictionIdentifier in parsedAfflictionIdentifiers)
{
if (!AfflictionPrefab.Prefabs.ContainsKey(afflictionIdentifier))
{
createWarningOrError($"Potentially invalid damage modifier in \"{parentDebugName}\". Could not find any afflictions with the identifier \"{afflictionIdentifier}\". Did you mean to use an affliction type instead?");
}
}
static void createWarningOrError(string msg)
{
#if DEBUG
DebugConsole.ThrowError(msg);
#else
DebugConsole.AddWarning(msg);
#endif
}
} }
private void ParseAfflictionTypes() private void ParseAfflictionTypes()
@@ -1,8 +1,4 @@
using Barotrauma.Items.Components; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Abilities namespace Barotrauma.Abilities
{ {
@@ -31,7 +27,7 @@ namespace Barotrauma.Abilities
} }
} }
if (!SelectedItemHasTag(closestCharacter)) { return; } if (closestCharacter == null || !SelectedItemHasTag(closestCharacter)) { return; }
if (closestDistance < squaredMaxDistance) if (closestDistance < squaredMaxDistance)
{ {
@@ -1267,7 +1267,11 @@ namespace Barotrauma
} }
}, () => }, () =>
{ {
return new[] { FactionPrefab.Prefabs.Select(f => f.Identifier.Value).ToArray() }; return new[]
{
FactionPrefab.Prefabs.Select(f => f.Identifier.Value).ToArray(),
GameMain.GameSession?.Campaign.Factions.Select(f => f.Prefab.Identifier.ToString()).ToArray() ?? Array.Empty<string>()
};
}, true)); }, true));
commands.Add(new Command("fixitems", "fixitems: Repairs all items and restores them to full condition.", (string[] args) => commands.Add(new Command("fixitems", "fixitems: Repairs all items and restores them to full condition.", (string[] args) =>
@@ -2235,7 +2239,7 @@ namespace Barotrauma
} }
} }
public static void ShowError(string msg, Color? color = null) public static void LogError(string msg, Color? color = null)
{ {
color ??= Color.Red; color ??= Color.Red;
NewMessage(msg, color.Value, isCommand: false, isError: true); NewMessage(msg, color.Value, isCommand: false, isError: true);
@@ -2407,7 +2411,7 @@ namespace Barotrauma
} }
#endif #endif
ShowError(error); LogError(error);
} }
public static void AddWarning(string warning) public static void AddWarning(string warning)
@@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -38,10 +38,12 @@ namespace Barotrauma
} }
IEnumerable<Affliction> afflictions = target.CharacterHealth.GetAllAfflictions().Where(affliction => IEnumerable<Affliction> afflictions = target.CharacterHealth.GetAllAfflictions().Where(affliction =>
{ {
LimbType? limbType = target.CharacterHealth.GetAfflictionLimb(affliction)?.type; if (affliction.Prefab.LimbSpecific)
if (limbType == null) { return false; } {
LimbType? limbType = target.CharacterHealth.GetAfflictionLimb(affliction)?.type;
return limbType == TargetLimb && affliction.Strength >= MinStrength; if (limbType == null || limbType != TargetLimb) { return false; }
}
return affliction.Strength >= MinStrength;
}); });
if (afflictions.Any(a => a.Identifier == Identifier)) { return true; } if (afflictions.Any(a => a.Identifier == Identifier)) { return true; }
@@ -13,7 +13,7 @@ namespace Barotrauma
{ {
if (TargetTag.IsEmpty) if (TargetTag.IsEmpty)
{ {
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no target tag! This will cause the check to automatically succeed."); DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no target tag! This will cause the check to automatically succeed.");
} }
foreach (var attribute in element.Attributes()) foreach (var attribute in element.Attributes())
{ {
@@ -25,7 +25,7 @@ namespace Barotrauma
} }
if (Conditional == null) if (Conditional == null)
{ {
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no valid PropertyConditional! This will cause the check to automatically succeed."); DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no valid PropertyConditional! This will cause the check to automatically succeed.");
} }
static bool IsTargetTagAttribute(XAttribute attribute) => attribute.NameAsIdentifier() == "targettag"; static bool IsTargetTagAttribute(XAttribute attribute) => attribute.NameAsIdentifier() == "targettag";
@@ -52,7 +52,7 @@ namespace Barotrauma
} }
if (target == null) if (target == null)
{ {
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction but no valid target was found for tag \"{TargetTag}\"! This will cause the check to automatically succeed."); DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction but no valid target was found for tag \"{TargetTag}\"! This will cause the check to automatically succeed.");
} }
if (target == null || Conditional == null) if (target == null || Conditional == null)
{ {
@@ -18,10 +18,14 @@ class CheckConnectionAction : BinaryOptionAction
[Serialize("", IsPropertySaveable.Yes)] [Serialize("", IsPropertySaveable.Yes)]
public Identifier OtherConnectionName { get; set; } public Identifier OtherConnectionName { get; set; }
[Serialize(1, IsPropertySaveable.Yes)]
public int MinAmount { get; set; }
public CheckConnectionAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { } public CheckConnectionAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
protected override bool? DetermineSuccess() protected override bool? DetermineSuccess()
{ {
int amount = 0;
var connectTargets = !ConnectedItemTag.IsEmpty ? ParentEvent.GetTargets(ConnectedItemTag) : Enumerable.Empty<Entity>(); var connectTargets = !ConnectedItemTag.IsEmpty ? ParentEvent.GetTargets(ConnectedItemTag) : Enumerable.Empty<Entity>();
foreach (var target in ParentEvent.GetTargets(ItemTag)) foreach (var target in ParentEvent.GetTargets(ItemTag))
{ {
@@ -33,27 +37,17 @@ class CheckConnectionAction : BinaryOptionAction
if (!IsCorrectConnection(connection, ConnectionName)) { continue; } if (!IsCorrectConnection(connection, ConnectionName)) { continue; }
if (ConnectedItemTag.IsEmpty && OtherConnectionName.IsEmpty) if (ConnectedItemTag.IsEmpty && OtherConnectionName.IsEmpty)
{ {
if (connection.Wires.Any()) { return true; } amount += connection.Wires.Count();
if (amount >= MinAmount) { return true; }
continue; continue;
} }
foreach (var wire in connection.Wires) foreach (var wire in connection.Wires)
{ {
if (wire.OtherConnection(connection) is not Connection otherConnection) { continue; } if (wire.OtherConnection(connection) is not Connection otherConnection) { continue; }
if (ConnectedItemTag.IsEmpty) if (!ConnectedItemTag.IsEmpty && !IsCorrectConnection(otherConnection, OtherConnectionName)) { continue; }
{ if (!ConnectedItemTag.IsEmpty && !IsCorrectItem()) { continue; }
if (IsCorrectConnection(otherConnection, OtherConnectionName)) { return true; } amount++;
} if (amount >= MinAmount) { return true; }
else if (OtherConnectionName.IsEmpty)
{
if (IsCorrectItem()) { return true; }
}
else
{
if (!IsCorrectConnection(otherConnection, OtherConnectionName)) { continue; }
if (!IsCorrectItem()) { continue; }
return true;
}
bool IsCorrectItem() => connectTargets.Contains(otherConnection.Item); bool IsCorrectItem() => connectTargets.Contains(otherConnection.Item);
} }
@@ -1,7 +1,6 @@
#nullable enable #nullable enable
using System; using System;
using System.Linq; using System.Linq;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -36,15 +35,32 @@ namespace Barotrauma
} }
} }
public CheckDataAction(ContentXElement element, string parentDebugString) : base(null, element)
{
if (string.IsNullOrEmpty(Condition))
{
Condition = element.GetAttributeString("value", string.Empty)!;
if (string.IsNullOrEmpty(Condition))
{
DebugConsole.ThrowError($"Error in scripted event \"{parentDebugString}\". CheckDataAction with no condition set ({element}).");
}
}
}
public bool GetSuccess()
{
return DetermineSuccess() ?? false;
}
protected override bool? DetermineSuccess() protected override bool? DetermineSuccess()
{ {
if (!(GameMain.GameSession?.GameMode is CampaignMode campaignMode)) { return false; } if (GameMain.GameSession?.GameMode is not CampaignMode campaignMode) { return false; }
string[] splitString = Condition.Split(' '); string[] splitString = Condition.Split(' ');
string value = Condition; string value;
if (splitString.Length > 0) if (splitString.Length > 0)
{ {
#warning Is this correct? //the first part of the string is the operator, skip it
value = string.Join(" ", splitString.Skip(1)); value = string.Join(" ", splitString.Skip(1));
} }
else else
@@ -1,4 +1,5 @@
using System; using Barotrauma.Items.Components;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -15,9 +16,20 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes)] [Serialize("", IsPropertySaveable.Yes)]
public string ItemTags { get; set; } public string ItemTags { get; set; }
[Serialize(1, IsPropertySaveable.Yes)]
public int Amount { get; set; }
[Serialize("", IsPropertySaveable.Yes, description: "Tag to apply to the first target when the check succeeds.")]
public Identifier ApplyTagToTarget { get; set; }
[Serialize(false, IsPropertySaveable.Yes)] [Serialize(false, IsPropertySaveable.Yes)]
public bool RequireEquipped { get; set; } public bool RequireEquipped { get; set; }
[Serialize(-1, IsPropertySaveable.Yes)]
public int ItemContainerIndex { get; set; }
private readonly IReadOnlyList<PropertyConditional> conditionals;
private readonly Identifier[] itemIdentifierSplit; private readonly Identifier[] itemIdentifierSplit;
private readonly Identifier[] itemTags; private readonly Identifier[] itemTags;
@@ -25,6 +37,19 @@ namespace Barotrauma
{ {
itemIdentifierSplit = ItemIdentifiers.Split(',').ToIdentifiers(); itemIdentifierSplit = ItemIdentifiers.Split(',').ToIdentifiers();
itemTags = ItemTags.Split(",").ToIdentifiers(); itemTags = ItemTags.Split(",").ToIdentifiers();
var conditionalList = new List<PropertyConditional>();
foreach (ContentXElement subElement in element.GetChildElements("conditional"))
{
foreach (XAttribute attribute in subElement.Attributes())
{
if (PropertyConditional.IsValid(attribute))
{
conditionalList.Add(new PropertyConditional(attribute));
}
}
break;
}
conditionals = conditionalList;
} }
protected override bool? DetermineSuccess() protected override bool? DetermineSuccess()
@@ -35,25 +60,70 @@ namespace Barotrauma
{ {
if (target is Character character) if (target is Character character)
{ {
if (RequireEquipped) Inventory inventory = character.Inventory;
if (CheckInventory(character.Inventory, character))
{ {
if (itemTags.Any(tag => character.HasEquippedItem(tag))) { return true; } if (!ApplyTagToTarget.IsEmpty)
if (itemIdentifierSplit.Any(identifier => character.HasEquippedItem(identifier))) { return true; } {
return false; ParentEvent.AddTarget(ApplyTagToTarget, target);
}
return true;
} }
if (character.Inventory is not CharacterInventory inventory) { continue; }
if (itemTags.Any(tag => inventory.FindItemByTag(tag, recursive: true) is not null)) { return true; }
if (itemIdentifierSplit.Any(identifier => inventory.FindItemByIdentifier(identifier, recursive: true) is not null)) { return true; }
} }
else if (target is Item item && item.OwnInventory is ItemInventory inventory) else if (target is Item item)
{ {
if (itemTags.Any(tag => inventory.FindItemByTag(tag, recursive: true) is not null)) { return true; } int i = 0;
if (itemIdentifierSplit.Any(identifier => inventory.FindItemByIdentifier(identifier, recursive: true) is not null)) { return true; } foreach (var itemContainer in item.GetComponents<ItemContainer>())
{
if (ItemContainerIndex == -1 || i == ItemContainerIndex)
{
if (CheckInventory(itemContainer.Inventory, character: null))
{
if (!ApplyTagToTarget.IsEmpty)
{
ParentEvent.AddTarget(ApplyTagToTarget, target);
}
return true;
}
}
i++;
}
} }
} }
return false; return false;
} }
private bool CheckInventory(Inventory inventory, Character character)
{
if (inventory == null) { return false; }
int count = 0;
foreach (Item item in inventory.FindAllItems(it => itemTags.Any(it.HasTag) || itemIdentifierSplit.Contains(it.Prefab.Identifier)))
{
if (!ConditionalsMatch(item, character)) { continue; }
count++;
if (count >= Amount) { return true; }
}
return false;
}
private bool ConditionalsMatch(Item item, Character character = null)
{
if (item == null) { return false; }
foreach (PropertyConditional conditional in conditionals)
{
if (!conditional.Matches(item))
{
return false;
}
}
if (RequireEquipped)
{
if (character == null) { return false; }
return character.HasEquippedItem(item);
}
return true;
}
public override string ToDebugString() public override string ToDebugString()
{ {
return $"{ToolBox.GetDebugSymbol(HasBeenDetermined())} {nameof(CheckItemAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " + return $"{ToolBox.GetDebugSymbol(HasBeenDetermined())} {nameof(CheckItemAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
@@ -11,6 +11,9 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes)] [Serialize("", IsPropertySaveable.Yes)]
public Identifier OrderOption { get; set; } public Identifier OrderOption { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier OrderTargetTag { get; set; }
public CheckOrderAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { } public CheckOrderAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
protected override bool? DetermineSuccess() protected override bool? DetermineSuccess()
@@ -29,20 +32,17 @@ namespace Barotrauma
} }
if (targetCharacter == null) if (targetCharacter == null)
{ {
DebugConsole.ShowError($"CheckConditionalAction error: {GetEventName()} uses a CheckOrderAction but no valid target character was found for tag \"{TargetTag}\"! This will cause the check to automatically fail."); DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckOrderAction but no valid target character was found for tag \"{TargetTag}\"! This will cause the check to automatically fail.");
return false; return false;
} }
var currentOrderInfo = targetCharacter.GetCurrentOrderWithTopPriority(); var currentOrderInfo = targetCharacter.GetCurrentOrderWithTopPriority();
if (currentOrderInfo?.Identifier == OrderIdentifier) if (currentOrderInfo?.Identifier == OrderIdentifier)
{ {
if (OrderOption.IsEmpty) if (!OrderTargetTag.IsEmpty)
{ {
return true; if (currentOrderInfo.TargetEntity is not Item targetItem || !targetItem.HasTag(OrderTargetTag)) { return false; }
}
else
{
return currentOrderInfo?.Option == OrderOption;
} }
return OrderOption.IsEmpty || currentOrderInfo?.Option == OrderOption;
} }
return false; return false;
} }
@@ -0,0 +1,89 @@
using Barotrauma.Extensions;
using System.Collections.Generic;
namespace Barotrauma
{
class CheckSelectedItemAction : BinaryOptionAction
{
public enum SelectedItemType { Primary, Secondary, Any };
[Serialize("", IsPropertySaveable.Yes)]
public Identifier CharacterTag { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier TargetTag { get; set; }
[Serialize(SelectedItemType.Any, IsPropertySaveable.Yes)]
public SelectedItemType ItemType { get; set; }
public CheckSelectedItemAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
protected override bool? DetermineSuccess()
{
Character character = null;
if (!CharacterTag.IsEmpty)
{
foreach (var t in ParentEvent.GetTargets(CharacterTag))
{
if (t is Character c)
{
character = c;
break;
}
}
}
if (character == null)
{
DebugConsole.LogError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid character was found for tag \"{CharacterTag}\"! This will cause the check to automatically fail.");
return false;
}
if (!TargetTag.IsEmpty)
{
IEnumerable<Entity> targets = ParentEvent.GetTargets(TargetTag);
if (targets.None())
{
DebugConsole.LogError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid targets were found for tag \"{TargetTag}\"! This will cause the check to automatically fail.");
return false;
}
foreach (var target in targets)
{
if (target is not Item targetItem)
{
continue;
}
if (IsSelected(targetItem))
{
return true;
}
}
return false;
bool IsSelected(Item item)
{
return ItemType switch
{
SelectedItemType.Any => character.IsAnySelectedItem(item),
SelectedItemType.Primary => character.SelectedItem == item,
SelectedItemType.Secondary => character.SelectedSecondaryItem == item,
_ => false
};
}
}
else
{
return ItemType switch
{
SelectedItemType.Any => !character.HasSelectedAnyItem,
SelectedItemType.Primary => character.SelectedItem == null,
SelectedItemType.Secondary => character.SelectedSecondaryItem == null,
_ => false
};
}
}
private string GetEventName()
{
return ParentEvent?.Prefab?.Identifier is { IsEmpty: false } identifier ? $"the event \"{identifier}\"" : "an unknown event";
}
}
}
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Barotrauma namespace Barotrauma
{ {
class CheckSelectedItemAction : BinaryOptionAction class CheckSelectedAction : BinaryOptionAction
{ {
public enum SelectedItemType { Primary, Secondary, Any }; public enum SelectedItemType { Primary, Secondary, Any };
@@ -16,7 +16,7 @@ namespace Barotrauma
[Serialize(SelectedItemType.Any, IsPropertySaveable.Yes)] [Serialize(SelectedItemType.Any, IsPropertySaveable.Yes)]
public SelectedItemType ItemType { get; set; } public SelectedItemType ItemType { get; set; }
public CheckSelectedItemAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { } public CheckSelectedAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
protected override bool? DetermineSuccess() protected override bool? DetermineSuccess()
{ {
@@ -34,7 +34,7 @@ namespace Barotrauma
} }
if (character == null) if (character == null)
{ {
DebugConsole.ShowError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid character was found for tag \"{CharacterTag}\"! This will cause the check to automatically fail."); DebugConsole.LogError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid character was found for tag \"{CharacterTag}\"! This will cause the check to automatically fail.");
return false; return false;
} }
if (!TargetTag.IsEmpty) if (!TargetTag.IsEmpty)
@@ -42,11 +42,16 @@ namespace Barotrauma
IEnumerable<Entity> targets = ParentEvent.GetTargets(TargetTag); IEnumerable<Entity> targets = ParentEvent.GetTargets(TargetTag);
if (targets.None()) if (targets.None())
{ {
DebugConsole.ShowError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid targets were found for tag \"{TargetTag}\"! This will cause the check to automatically fail."); DebugConsole.LogError($"CheckSelectedItemAction error: {GetEventName()} uses a CheckSelectedItemAction but no valid targets were found for tag \"{TargetTag}\"! This will cause the check to automatically fail.");
return false; return false;
} }
foreach (var target in targets) foreach (var target in targets)
{ {
if (target is Character targetCharacter)
{
if (ItemType == SelectedItemType.Any && character.SelectedCharacter == targetCharacter) { return true; }
continue;
}
if (target is not Item targetItem) if (target is not Item targetItem)
{ {
continue; continue;
@@ -134,7 +134,7 @@ namespace Barotrauma
public static EventAction Instantiate(ScriptedEvent scriptedEvent, ContentXElement element) public static EventAction Instantiate(ScriptedEvent scriptedEvent, ContentXElement element)
{ {
Type actionType = null; Type actionType;
try try
{ {
actionType = Type.GetType("Barotrauma." + element.Name, true, true); actionType = Type.GetType("Barotrauma." + element.Name, true, true);
@@ -1,8 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
class GodModeAction : EventAction class GodModeAction : EventAction
@@ -10,6 +5,9 @@ namespace Barotrauma
[Serialize(true, IsPropertySaveable.Yes)] [Serialize(true, IsPropertySaveable.Yes)]
public bool Enabled { get; set; } public bool Enabled { get; set; }
[Serialize(false, IsPropertySaveable.Yes, description: "Should the character's active afflictions be updated (e.g. applying visual effects of the afflictions)")]
public bool UpdateAfflictions { get; set; }
[Serialize("", IsPropertySaveable.Yes)] [Serialize("", IsPropertySaveable.Yes)]
public Identifier TargetTag { get; set; } public Identifier TargetTag { get; set; }
@@ -35,7 +33,14 @@ namespace Barotrauma
{ {
if (target != null && target is Character character) if (target != null && target is Character character)
{ {
character.GodMode = Enabled; if (UpdateAfflictions)
{
character.CharacterHealth.Unkillable = Enabled;
}
else
{
character.GodMode = Enabled;
}
} }
} }
isFinished = true; isFinished = true;
@@ -0,0 +1,33 @@
namespace Barotrauma;
partial class InventoryHighlightAction : EventAction
{
[Serialize("", IsPropertySaveable.Yes)]
public Identifier TargetTag { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier ItemIdentifier { get; set; }
[Serialize(-1, IsPropertySaveable.Yes)]
public int ItemContainerIndex { get; set; }
[Serialize(false, IsPropertySaveable.Yes)]
public bool Recursive { get; set; }
private bool isFinished;
public InventoryHighlightAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
public override void Update(float deltaTime)
{
if (isFinished) { return; }
UpdateProjSpecific();
isFinished = true;
}
partial void UpdateProjSpecific();
public override bool IsFinished(ref string goToLabel) => isFinished;
public override void Reset() => isFinished = false;
}
@@ -2,7 +2,7 @@ namespace Barotrauma
{ {
partial class MessageBoxAction : EventAction partial class MessageBoxAction : EventAction
{ {
public enum ActionType { Create, Close } public enum ActionType { Create, ConnectObjective, Close, Clear }
[Serialize(ActionType.Create, IsPropertySaveable.Yes)] [Serialize(ActionType.Create, IsPropertySaveable.Yes)]
public ActionType Type { get; set; } public ActionType Type { get; set; }
@@ -51,7 +51,13 @@ namespace Barotrauma
private bool isFinished = false; private bool isFinished = false;
public MessageBoxAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { } public MessageBoxAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
if (Identifier.IsEmpty)
{
Identifier = element.GetAttributeIdentifier("id", Identifier.Empty);
}
}
public override void Update(float deltaTime) public override void Update(float deltaTime)
{ {
@@ -55,7 +55,7 @@ namespace Barotrauma
if (GameMain.GameSession.GameMode is CampaignMode campaign) if (GameMain.GameSession.GameMode is CampaignMode campaign)
{ {
MissionPrefab prefab = null; Mission unlockedMission = null;
var unlockLocation = FindUnlockLocation(); var unlockLocation = FindUnlockLocation();
if (unlockLocation == null && CreateLocationIfNotFound) if (unlockLocation == null && CreateLocationIfNotFound)
{ {
@@ -72,27 +72,34 @@ namespace Barotrauma
{ {
if (!MissionIdentifier.IsEmpty) if (!MissionIdentifier.IsEmpty)
{ {
prefab = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier); unlockedMission = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier);
} }
else if (!MissionTag.IsEmpty) else if (!MissionTag.IsEmpty)
{ {
prefab = unlockLocation.UnlockMissionByTag(MissionTag); unlockedMission = unlockLocation.UnlockMissionByTag(MissionTag);
} }
if (campaign is MultiPlayerCampaign mpCampaign) if (campaign is MultiPlayerCampaign mpCampaign)
{ {
mpCampaign.IncrementLastUpdateIdForFlag(MultiPlayerCampaign.NetFlags.MapAndMissions); mpCampaign.IncrementLastUpdateIdForFlag(MultiPlayerCampaign.NetFlags.MapAndMissions);
} }
if (prefab != null) if (unlockedMission != null)
{ {
DebugConsole.NewMessage($"Unlocked mission \"{prefab.Name}\" in the location \"{unlockLocation.Name}\"."); if (unlockedMission.Locations[0] == unlockedMission.Locations[1] || unlockedMission.Locations[1] ==null)
#if CLIENT
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", prefab.Name),
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
{ {
IconColor = prefab.IconColor DebugConsole.NewMessage($"Unlocked mission \"{unlockedMission.Name}\" in the location \"{unlockLocation.Name}\".");
}
else
{
DebugConsole.NewMessage($"Unlocked mission \"{unlockedMission.Name}\" in the connection from \"{unlockedMission.Locations[0].Name}\" to \"{unlockedMission.Locations[1].Name}\".");
}
#if CLIENT
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", unlockedMission.Name),
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: unlockedMission.Prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
{
IconColor = unlockedMission.Prefab.IconColor
}; };
#else #else
NotifyMissionUnlock(prefab); NotifyMissionUnlock(unlockedMission);
#endif #endif
} }
} }
@@ -140,14 +147,15 @@ namespace Barotrauma
} }
#if SERVER #if SERVER
private void NotifyMissionUnlock(MissionPrefab prefab) private void NotifyMissionUnlock(Mission mission)
{ {
foreach (Client client in GameMain.Server.ConnectedClients) foreach (Client client in GameMain.Server.ConnectedClients)
{ {
IWriteMessage outmsg = new WriteOnlyMessage(); IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION); outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
outmsg.WriteByte((byte)EventManager.NetworkEventType.MISSION); outmsg.WriteByte((byte)EventManager.NetworkEventType.MISSION);
outmsg.WriteIdentifier(prefab.Identifier); outmsg.WriteIdentifier(mission.Prefab.Identifier);
outmsg.WriteString(mission.Name.Value);
GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable); GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
} }
} }
@@ -16,6 +16,9 @@ namespace Barotrauma
[Serialize(false, IsPropertySaveable.Yes)] [Serialize(false, IsPropertySaveable.Yes)]
public bool AddToCrew { get; set; } public bool AddToCrew { get; set; }
[Serialize(false, IsPropertySaveable.Yes)]
public bool RemoveFromCrew { get; set; }
private bool isFinished = false; private bool isFinished = false;
public NPCChangeTeamAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { } public NPCChangeTeamAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
@@ -35,34 +38,47 @@ namespace Barotrauma
if (AddToCrew && (TeamTag == CharacterTeamType.Team1 || TeamTag == CharacterTeamType.Team2)) if (AddToCrew && (TeamTag == CharacterTeamType.Team1 || TeamTag == CharacterTeamType.Team2))
{ {
npc.Info.StartItemsGiven = true; npc.Info.StartItemsGiven = true;
GameMain.GameSession.CrewManager.AddCharacter(npc); GameMain.GameSession.CrewManager.AddCharacter(npc);
ChangeItemTeam(Submarine.MainSub, true);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.AddToCrewEventData(TeamTag, npc.Inventory.AllItems));
}
}
else if (RemoveFromCrew && (npc.TeamID == CharacterTeamType.Team1 || npc.TeamID == CharacterTeamType.Team2))
{
npc.Info.StartItemsGiven = true;
GameMain.GameSession.CrewManager.RemoveCharacter(npc, removeInfo: true);
var sub = Submarine.Loaded.FirstOrDefault(s => s.TeamID == TeamTag);
ChangeItemTeam(sub, false);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.RemoveFromCrewEventData(TeamTag, npc.Inventory.AllItems));
}
}
void ChangeItemTeam(Submarine sub, bool allowStealing)
{
foreach (Item item in npc.Inventory.AllItems) foreach (Item item in npc.Inventory.AllItems)
{ {
item.AllowStealing = true; item.AllowStealing = allowStealing;
var wifiComponent = item.GetComponent<Items.Components.WifiComponent>(); if (item.GetComponent<Items.Components.WifiComponent>() is { } wifiComponent)
if (wifiComponent != null)
{ {
wifiComponent.TeamID = TeamTag; wifiComponent.TeamID = TeamTag;
} }
var idCard = item.GetComponent<Items.Components.IdCard>(); if (item.GetComponent<Items.Components.IdCard>() is { } idCard)
if (idCard != null)
{ {
idCard.TeamID = TeamTag; idCard.TeamID = TeamTag;
idCard.SubmarineSpecificID = 0; idCard.SubmarineSpecificID = 0;
} }
} }
WayPoint subWaypoint = WayPoint subWaypoint =
WayPoint.WayPointList.Find(wp => wp.Submarine == Submarine.MainSub && wp.SpawnType == SpawnType.Human && wp.AssignedJob == npc.Info.Job?.Prefab) ?? WayPoint.WayPointList.Find(wp => wp.Submarine == sub && wp.SpawnType == SpawnType.Human && wp.AssignedJob == npc.Info.Job?.Prefab) ??
WayPoint.WayPointList.Find(wp => wp.Submarine == Submarine.MainSub && wp.SpawnType == SpawnType.Human); WayPoint.WayPointList.Find(wp => wp.Submarine == sub && wp.SpawnType == SpawnType.Human);
if (subWaypoint != null) if (subWaypoint != null)
{ {
npc.GiveIdCardTags(subWaypoint, createNetworkEvent: true); npc.GiveIdCardTags(subWaypoint, createNetworkEvent: true);
} }
#if SERVER
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.AddToCrewEventData(TeamTag, npc.Inventory.AllItems));
#endif
} }
} }
isFinished = true; isFinished = true;
@@ -1,8 +1,5 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -17,6 +14,12 @@ namespace Barotrauma
[Serialize(true, IsPropertySaveable.Yes)] [Serialize(true, IsPropertySaveable.Yes)]
public bool Follow { get; set; } public bool Follow { get; set; }
[Serialize(-1, IsPropertySaveable.Yes)]
public int MaxTargets { get; set; }
[Serialize(true, IsPropertySaveable.Yes)]
public bool AbandonOnReset { get; set; }
private bool isFinished = false; private bool isFinished = false;
public NPCFollowAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { } public NPCFollowAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
@@ -32,6 +35,7 @@ namespace Barotrauma
target = ParentEvent.GetTargets(TargetTag).FirstOrDefault(); target = ParentEvent.GetTargets(TargetTag).FirstOrDefault();
if (target == null) { return; } if (target == null) { return; }
int targetCount = 0;
affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList(); affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList();
foreach (var npc in affectedNpcs) foreach (var npc in affectedNpcs)
{ {
@@ -56,6 +60,11 @@ namespace Barotrauma
} }
} }
} }
targetCount++;
if (MaxTargets > -1 && targetCount >= MaxTargets)
{
break;
}
} }
isFinished = true; isFinished = true;
} }
@@ -67,11 +76,11 @@ namespace Barotrauma
public override void Reset() public override void Reset()
{ {
if (affectedNpcs != null && target != null) if (affectedNpcs != null && target != null && AbandonOnReset)
{ {
foreach (var npc in affectedNpcs) foreach (var npc in affectedNpcs)
{ {
if (npc.Removed || !(npc.AIController is HumanAIController humanAiController)) { continue; } if (npc.Removed || npc.AIController is not HumanAIController humanAiController) { continue; }
foreach (var goToObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveGoTo>()) foreach (var goToObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveGoTo>())
{ {
if (goToObjective.Target == target) if (goToObjective.Target == target)
@@ -0,0 +1,122 @@
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
class NPCOperateItemAction : EventAction
{
[Serialize("", IsPropertySaveable.Yes)]
public Identifier NPCTag { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier TargetTag { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier ItemComponentName { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier OrderOption { get; set; }
[Serialize(false, IsPropertySaveable.Yes)]
public bool RequireEquip { get; set; }
[Serialize(true, IsPropertySaveable.Yes)]
public bool Operate { get; set; }
[Serialize(-1, IsPropertySaveable.Yes)]
public int MaxTargets { get; set; }
[Serialize(true, IsPropertySaveable.Yes)]
public bool AbandonOnReset { get; set; }
private bool isFinished = false;
public NPCOperateItemAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
private List<Character> affectedNpcs = null;
private Item target = null;
public override void Update(float deltaTime)
{
if (isFinished) { return; }
target = ParentEvent.GetTargets(TargetTag).FirstOrDefault() as Item;
if (target == null) { return; }
int targetCount = 0;
affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList();
foreach (var npc in affectedNpcs)
{
if (npc.AIController is not HumanAIController humanAiController) { continue; }
if (Operate)
{
ItemComponentName = "Controller".ToIdentifier();
var itemComponent = target.Components.FirstOrDefault(ic => ItemComponentName == ic.Name);
if (itemComponent == null)
{
DebugConsole.AddWarning($"Error in NPCOperateItemAction: could not find the component \"{ItemComponentName}\" in item \"{target.Name}\".");
}
else
{
var newObjective = new AIObjectiveOperateItem(itemComponent, npc, humanAiController.ObjectiveManager, OrderOption, RequireEquip)
{
OverridePriority = 100.0f
};
humanAiController.ObjectiveManager.AddObjective(newObjective);
humanAiController.ObjectiveManager.WaitTimer = 0.0f;
humanAiController.ObjectiveManager.Objectives.RemoveAll(o => o is AIObjectiveGoTo gotoOjective);
}
}
else
{
foreach (var objective in humanAiController.ObjectiveManager.Objectives)
{
if (objective is AIObjectiveOperateItem operateItemObjective && operateItemObjective.OperateTarget == target)
{
objective.Abandon = true;
}
}
}
targetCount++;
if (MaxTargets > -1 && targetCount >= MaxTargets)
{
break;
}
}
isFinished = true;
}
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
if (affectedNpcs != null && target != null && AbandonOnReset)
{
foreach (var npc in affectedNpcs)
{
if (npc.Removed || npc.AIController is not HumanAIController humanAiController) { continue; }
foreach (var operateItemObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveOperateItem>())
{
if (operateItemObjective.OperateTarget == target)
{
operateItemObjective.Abandon = true;
}
}
}
target = null;
affectedNpcs = null;
}
isFinished = false;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(AIObjectiveOperateItem)} -> (NPCTag: {NPCTag.ColorizeObject()}, TargetTag: {TargetTag.ColorizeObject()}, Operate: {Operate.ColorizeObject()})";
}
}
}
@@ -29,7 +29,7 @@ namespace Barotrauma
foreach (var npc in affectedNpcs) foreach (var npc in affectedNpcs)
{ {
if (!(npc.AIController is HumanAIController humanAiController)) { continue; } if (npc.AIController is not HumanAIController humanAiController) { continue; }
if (Wait) if (Wait)
{ {
@@ -62,7 +62,7 @@ namespace Barotrauma
{ {
foreach (var npc in affectedNpcs) foreach (var npc in affectedNpcs)
{ {
if (npc.Removed || !(npc.AIController is HumanAIController)) { continue; } if (npc.Removed || npc.AIController is not HumanAIController) { continue; }
if (gotoObjective != null) if (gotoObjective != null)
{ {
gotoObjective.Abandon = true; gotoObjective.Abandon = true;
@@ -60,6 +60,9 @@ namespace Barotrauma
[Serialize(true, IsPropertySaveable.Yes, description: "If false, we won't spawn another character if one with the same identifier has already been spawned.")] [Serialize(true, IsPropertySaveable.Yes, description: "If false, we won't spawn another character if one with the same identifier has already been spawned.")]
public bool AllowDuplicates { get; set; } public bool AllowDuplicates { get; set; }
[Serialize(100.0f, IsPropertySaveable.Yes)]
public float Offset { get; set; }
[Serialize("", IsPropertySaveable.Yes, "What outpost module tags does the entity prefer to spawn in.")] [Serialize("", IsPropertySaveable.Yes, "What outpost module tags does the entity prefer to spawn in.")]
public string TargetModuleTags public string TargetModuleTags
{ {
@@ -127,7 +130,7 @@ namespace Barotrauma
ISpatialEntity spawnPos = GetSpawnPos(); ISpatialEntity spawnPos = GetSpawnPos();
if (spawnPos != null) if (spawnPos != null)
{ {
Entity.Spawner.AddCharacterToSpawnQueue(CharacterPrefab.HumanSpeciesName, OffsetSpawnPos(spawnPos.WorldPosition, 100.0f), humanPrefab.CreateCharacterInfo(), onSpawn: newCharacter => Entity.Spawner.AddCharacterToSpawnQueue(CharacterPrefab.HumanSpeciesName, OffsetSpawnPos(spawnPos.WorldPosition, Offset), humanPrefab.CreateCharacterInfo(), onSpawn: newCharacter =>
{ {
if (newCharacter == null) { return; } if (newCharacter == null) { return; }
newCharacter.HumanPrefab = humanPrefab; newCharacter.HumanPrefab = humanPrefab;
@@ -162,7 +165,7 @@ namespace Barotrauma
ISpatialEntity spawnPos = GetSpawnPos(); ISpatialEntity spawnPos = GetSpawnPos();
if (spawnPos != null) if (spawnPos != null)
{ {
Entity.Spawner.AddCharacterToSpawnQueue(SpeciesName, OffsetSpawnPos(spawnPos.WorldPosition, 100.0f), onSpawn: newCharacter => Entity.Spawner.AddCharacterToSpawnQueue(SpeciesName, OffsetSpawnPos(spawnPos.WorldPosition, Offset), onSpawn: newCharacter =>
{ {
if (!TargetTag.IsEmpty && newCharacter != null) if (!TargetTag.IsEmpty && newCharacter != null)
{ {
@@ -208,7 +211,7 @@ namespace Barotrauma
ISpatialEntity spawnPos = GetSpawnPos(); ISpatialEntity spawnPos = GetSpawnPos();
if (spawnPos != null) if (spawnPos != null)
{ {
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, OffsetSpawnPos(spawnPos.WorldPosition, 100.0f), onSpawned: onSpawned); Entity.Spawner.AddItemToSpawnQueue(itemPrefab, OffsetSpawnPos(spawnPos.WorldPosition, Offset), onSpawned: onSpawned);
} }
} }
else else
@@ -13,7 +13,7 @@ class TutorialIconAction : EventAction
public Identifier TargetTag { get; set; } public Identifier TargetTag { get; set; }
[Serialize("", IsPropertySaveable.Yes)] [Serialize("", IsPropertySaveable.Yes)]
public string IconStyle { get; set; } public Identifier IconStyle { get; set; }
private bool isFinished; private bool isFinished;
@@ -33,7 +33,7 @@ class TutorialIconAction : EventAction
} }
else if(Type == ActionType.Remove) else if(Type == ActionType.Remove)
{ {
tutorialMode.Tutorial?.Icons.RemoveAll(i => i.entity == target && i.iconStyle.Equals(IconStyle, System.StringComparison.OrdinalIgnoreCase)); tutorialMode.Tutorial?.Icons.RemoveAll(i => i.entity == target && i.iconStyle == IconStyle);
} }
else if (Type == ActionType.RemoveTarget) else if (Type == ActionType.RemoveTarget)
{ {
@@ -41,7 +41,7 @@ class TutorialIconAction : EventAction
} }
else if (Type == ActionType.RemoveIcon) else if (Type == ActionType.RemoveIcon)
{ {
tutorialMode.Tutorial?.Icons.RemoveAll(i => i.iconStyle.Equals(IconStyle, System.StringComparison.OrdinalIgnoreCase)); tutorialMode.Tutorial?.Icons.RemoveAll(i => i.iconStyle == IconStyle);
} }
else if (Type == ActionType.Clear) else if (Type == ActionType.Clear)
{ {
@@ -8,7 +8,7 @@ namespace Barotrauma
public SegmentActionType Type { get; set; } public SegmentActionType Type { get; set; }
[Serialize("", IsPropertySaveable.Yes)] [Serialize("", IsPropertySaveable.Yes)]
public Identifier Id { get; set; } public Identifier Identifier { get; set; }
[Serialize("", IsPropertySaveable.Yes)] [Serialize("", IsPropertySaveable.Yes)]
public Identifier ObjectiveTag { get; set; } public Identifier ObjectiveTag { get; set; }
@@ -30,7 +30,13 @@ namespace Barotrauma
private bool isFinished; private bool isFinished;
public TutorialSegmentAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { } public TutorialSegmentAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
if (Identifier.IsEmpty)
{
Identifier = element.GetAttributeIdentifier("id", Identifier.Empty);
}
}
public override void Update(float deltaTime) public override void Update(float deltaTime)
{ {

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