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

View File

@@ -399,12 +399,9 @@ namespace Barotrauma
progressBar.Draw(spriteBatch, cam);
}
void DrawInteractionIcon(Entity entity, string iconStyle)
void DrawInteractionIcon(Entity entity, Identifier iconStyle)
{
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
{
@@ -412,20 +409,28 @@ namespace Barotrauma
Item item => item.CurrentHull,
_ => null
};
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
//a check for InteractionType.Examine here.
if (characterEntity.IsDead || characterEntity.IsIncapacitated) { return; }
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);
if (body != null && body.UserData != entity) { return; }
var body = Submarine.CheckVisibility(character.SimPosition, entity.SimPosition, ignoreLevel: true);
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 distFactor = 1.0f - MathUtils.InverseLerp(1000.0f, 3000.0f, dist);
float alpha = MathHelper.Lerp(0.3f, 1.0f, distFactor);
@@ -436,13 +441,13 @@ namespace Barotrauma
visibleRange,
style.GetDefaultSprite(),
style.Color * alpha,
label: characterEntity?.Info?.Title);
label: label);
}
foreach (Character npc in Character.CharacterList)
{
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)

View File

@@ -2,6 +2,7 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma
@@ -141,7 +142,7 @@ namespace Barotrauma
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);
switch (eventData)
@@ -471,25 +472,11 @@ namespace Barotrauma
break;
case EventType.AddToCrew:
GameMain.GameSession.CrewManager.AddCharacter(this);
CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte();
ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++)
{
ushort itemID = msg.ReadUInt16();
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;
}
}
ReadItemTeamChange(msg, true);
break;
case EventType.RemoveFromCrew:
GameMain.GameSession.CrewManager.RemoveCharacter(this, removeInfo: true);
ReadItemTeamChange(msg, false);
break;
case EventType.UpdateExperience:
int experienceAmount = msg.ReadInt32();
@@ -520,9 +507,27 @@ namespace Barotrauma
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
}
break;
}
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)

View File

@@ -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")
{
UserData = UIHighlightAction.ElementId.CPRButton,
OnClicked = (button, userData) =>
{
Character selectedCharacter = Character.Controlled?.SelectedCharacter;
@@ -690,7 +691,7 @@ namespace Barotrauma
{
distortTimer = (distortTimer + deltaTime * distortSpeed) % MathHelper.TwoPi;
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
{
@@ -1294,6 +1295,7 @@ namespace Barotrauma
Dictionary<Identifier, float> treatmentSuitability = new Dictionary<Identifier, float>();
GetSuitableTreatments(treatmentSuitability,
normalize: true,
user: Character.Controlled,
ignoreHiddenAfflictions: true,
limb: selectedLimbIndex == -1 ? null : Character.AnimController.Limbs.Find(l => l.HealthIndex == selectedLimbIndex));
@@ -1337,13 +1339,13 @@ namespace Barotrauma
};
var innerFrame = new GUIButton(new RectTransform(Vector2.One, itemSlot.RectTransform, Anchor.Center, Pivot.Center, scaleBasis: ScaleBasis.Smallest), style: "SubtreeHeader")
{
{
UserData = item,
DisabledColor = Color.White * 0.1f,
PlaySoundOnSelect = false,
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);
if (item == null) { return false; }
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex);
@@ -1900,6 +1902,7 @@ namespace Barotrauma
public void ClientRead(IReadMessage inc)
{
newAfflictions.Clear();
newPeriodicEffects.Clear();
byte afflictionCount = inc.ReadByte();
for (int i = 0; i < afflictionCount; i++)
{
@@ -1921,7 +1924,7 @@ namespace Barotrauma
int periodicAfflictionCount = inc.ReadByte();
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));
}
newAfflictions.Add((null, afflictionPrefab, afflictionStrength));
@@ -1991,13 +1994,13 @@ namespace Barotrauma
//timer has wrapped around, apply the effect
if (periodicEffect.timer - existingAffliction.PeriodicEffectTimers[periodicEffect.effect] > periodicEffect.effect.MinInterval / 2)
{
existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer;
foreach (StatusEffect effect in periodicEffect.effect.StatusEffects)
{
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == limbHealths.IndexOf(limb));
existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: targetLimb);
}
}
existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer;
}
}

View File

@@ -92,11 +92,13 @@ namespace Barotrauma
}
}
float prevSize = conversationList.TotalSize;
List<GUIButton> extraButtons = CreateConversation(conversationList, text, speaker, options, string.IsNullOrWhiteSpace(spriteIdentifier));
AssignActionsToButtons(extraButtons, lastMessageBox);
RecalculateLastMessage(conversationList, true);
conversationList.ScrollToEnd(0.5f);
conversationList.BarScroll = (prevSize - conversationList.Content.Rect.Height) / (conversationList.TotalSize - conversationList.Content.Rect.Height);
conversationList.ScrollToEnd(duration: 0.5f);
lastMessageBox.SetBackgroundIcon(eventSprite);
return;
}
@@ -112,7 +114,7 @@ namespace Barotrauma
};
messageBox.OnAddedToGUIUpdateList += (GUIComponent component) =>
{
if (!(Screen.Selected is GameScreen)) { messageBox.Close(); }
if (Screen.Selected is not GameScreen) { messageBox.Close(); }
};
lastMessageBox = messageBox;
@@ -321,7 +323,7 @@ namespace Barotrauma
AlwaysOverrideCursor = true
};
LocalizedString translatedText = TextManager.Get(text).Fallback(text);
LocalizedString translatedText = TextManager.ParseInputTypes(TextManager.Get(text)).Fallback(text);
if (speaker?.Info != null && drawChathead)
{

View File

@@ -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)));
}
}

View File

@@ -8,20 +8,24 @@ partial class MessageBoxAction : EventAction
{
partial void UpdateProjSpecific()
{
if (Type == ActionType.Create)
if (Type == ActionType.Create || Type == ActionType.ConnectObjective)
{
CreateMessageBox();
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);
tutorialMode.Tutorial?.TriggerTutorialSegment(segment);
tutorialMode.Tutorial?.TriggerTutorialSegment(segment, connectObjective: Type == ActionType.ConnectObjective);
}
}
else if (Type == ActionType.Close)
{
GUIMessageBox.Close(Tag);
}
else if (Type == ActionType.Clear)
{
GUIMessageBox.CloseAll();
}
}
public void CreateMessageBox()

View File

@@ -4,7 +4,7 @@ namespace Barotrauma;
partial class TutorialHighlightAction : EventAction
{
private static readonly Color highlightColor = Color.OrangeRed;
private static readonly Color highlightColor = Color.Orange;
partial void UpdateProjSpecific()
{
@@ -19,32 +19,32 @@ partial class TutorialHighlightAction : EventAction
{
if (entity is Item i)
{
SetHighlight(i);
SetItemHighlight(i);
}
else if (entity is Structure s)
{
SetHighlight(s);
SetStructureHighlight(s);
}
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; }
item.SpriteColor = (State) ? highlightColor : Color.White;
item.HighlightColor = State ? highlightColor : null;
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;
}
private void SetHighlight(Character character)
private void SetCharacterHighlight(Character character)
{
character.ExternalHighlight = State;
}

View File

@@ -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)
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.Video(VideoFile, TextTag, Width, Height));
}
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)
{
@@ -30,21 +30,21 @@ partial class TutorialSegmentAction : EventAction
tutorial.TriggerTutorialSegment(segment);
break;
case SegmentActionType.Complete:
tutorial.CompleteTutorialSegment(Id);
tutorial.CompleteTutorialSegment(Identifier);
break;
case SegmentActionType.Remove:
tutorial.RemoveTutorialSegment(Id);
tutorial.RemoveTutorialSegment(Identifier);
break;
case SegmentActionType.CompleteAndRemove:
tutorial.CompleteTutorialSegment(Id);
tutorial.RemoveTutorialSegment(Id);
tutorial.CompleteTutorialSegment(Identifier);
tutorial.RemoveTutorialSegment(Identifier);
break;
}
}
}
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!");
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -651,11 +651,11 @@ namespace Barotrauma
break;
case NetworkEventType.MISSION:
Identifier missionIdentifier = msg.ReadIdentifier();
string missionName = msg.ReadString();
MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier);
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))
{
IconColor = prefab.IconColor

View File

@@ -410,7 +410,7 @@ namespace Barotrauma
{
y += yStep;
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);
}
@@ -879,6 +879,11 @@ namespace Barotrauma
}
}
}
public static IEnumerable<GUIComponent> GetAdditions()
{
return additions;
}
#endregion
public static GUIComponent MouseOn { get; private set; }

View File

@@ -486,7 +486,7 @@ namespace Barotrauma
{
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)
{
bounceJump += deltaTime * 4;
@@ -503,6 +503,7 @@ namespace Barotrauma
bounceJump = 0.0f;
bounceTimer = 0.0f;
bounceDown = false;
Bounce = false;
}
}
}

View File

@@ -85,11 +85,11 @@ namespace Barotrauma
get { return sprite; }
set
{
if (sprite == value) return;
if (sprite == value) { return; }
sprite = value;
sourceRect = value == null ? Rectangle.Empty : value.SourceRect;
origin = value == null ? Vector2.Zero : value.size / 2;
if (scaleToFit) RecalculateScale();
if (scaleToFit) { RecalculateScale(); }
}
}

View File

@@ -601,16 +601,13 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
float t = 0.0f;
float startScroll = BarScroll * BarSize;
float startScroll = BarScroll;
float distanceToTravel = ScrollBar.MaxValue - startScroll;
float progress = startScroll;
float speed = distanceToTravel / duration;
while (t < duration && !MathUtils.NearlyEqual(ScrollBar.MaxValue, progress))
while (t < duration && !MathUtils.NearlyEqual(ScrollBar.MaxValue, BarScroll))
{
t += CoroutineManager.DeltaTime;
progress += speed * CoroutineManager.DeltaTime;
BarScroll = progress;
BarScroll += speed * CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}

View File

@@ -207,7 +207,7 @@ namespace Barotrauma
{
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
CanBeFocused = false;
AutoClose = true;
AutoClose = type == Type.InGame;
GUIStyle.Apply(InnerFrame, "", this);
var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), InnerFrame.RectTransform, Anchor.Center),

View File

@@ -160,8 +160,13 @@ namespace Barotrauma
public static Point ItemFrameOffset => new Point(0, 3).Multiply(GUI.SlicedSpriteScale);
public static GUIComponentStyle GetComponentStyle(string name)
=> ComponentStyles.TryGet(name, out var style) ? style : null;
public static GUIComponentStyle GetComponentStyle(string styleName)
{
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)
{

View File

@@ -238,7 +238,7 @@ namespace Barotrauma
errorId = "Store.SelectStore:StoreDoesntExist";
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);
}
}
@@ -263,7 +263,7 @@ namespace Barotrauma
}
if (!msg.IsNullOrEmpty())
{
DebugConsole.ShowError(msg);
DebugConsole.LogError(msg);
GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg);
}
}
@@ -1250,7 +1250,7 @@ namespace Barotrauma
}
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()}";
#if DEBUG
DebugConsole.ShowError(errorMsg);
DebugConsole.LogError(errorMsg);
#else
DebugConsole.AddWarning(errorMsg);
#endif
@@ -1882,7 +1882,7 @@ namespace Barotrauma
}
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)
{
@@ -1962,7 +1962,7 @@ namespace Barotrauma
}
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;
}
}
@@ -1982,7 +1982,7 @@ namespace Barotrauma
}
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;
}
}
@@ -2029,7 +2029,7 @@ namespace Barotrauma
}
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;
}
var itemsToRemove = new List<PurchasedItem>();

View File

@@ -206,9 +206,9 @@ namespace Barotrauma
transferMenuButton.RectTransform.AbsoluteOffset = new Point(0, -pos - transferMenu.Rect.Height);
}
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;
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);
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; }
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();
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();

View File

@@ -874,7 +874,7 @@ namespace Barotrauma
itemPrefab.SwappableItem.CanBeBought &&
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)
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);
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())));
}
@@ -982,7 +982,7 @@ namespace Barotrauma
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,
price, replacement,
@@ -998,7 +998,7 @@ namespace Barotrauma
{
LocalizedString promptBody = TextManager.GetWithVariables(isPurchased ? "upgrades.itemswappromptbody" : "upgrades.purchaseitemswappromptbody",
("[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, () =>
{
if (GameMain.NetworkMember != null)
@@ -1042,7 +1042,7 @@ namespace Barotrauma
}
if (toggleButton.Selected)
{
var linkedItems = Campaign.UpgradeManager.GetLinkedItemsToSwap(item);
var linkedItems = UpgradeManager.GetLinkedItemsToSwap(item);
foreach (var itemPreview in itemPreviews)
{
itemPreview.Value.OutlineColor = itemPreview.Value.Color = linkedItems.Contains(itemPreview.Key) ? GUIStyle.Orange : previewWhite;
@@ -1391,7 +1391,7 @@ namespace Barotrauma
{
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 (!itemElement.Selected) { itemElement.OnClicked(itemElement, itemElement.UserData); }

View File

@@ -139,7 +139,7 @@ namespace Barotrauma
}
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;
}
bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
@@ -148,7 +148,7 @@ namespace Barotrauma
var sellValues = GetSellValuesAtCurrentLocation(storeIdentifier, itemsToSell.Select(i => i.ItemPrefab));
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;
}
var storeSpecificSoldItems = GetSoldItems(storeIdentifier, create: true);

View File

@@ -291,29 +291,13 @@ namespace Barotrauma
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>
/// Add character to the list without actually adding it to the crew
/// </summary>
public GUIComponent AddCharacterToCrewList(Character character)
{
if (character == null) { return null; }
if (crewList.Content.Children.Any(c => c.UserData as Character == character)) { return null; }
var background = new GUIFrame(
new RectTransform(crewListEntrySize, parent: crewList.Content.RectTransform, anchor: Anchor.TopRight),
@@ -509,7 +493,15 @@ namespace Barotrauma
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 (character.Info?.Job?.Prefab == null) { return; }
@@ -2821,7 +2813,7 @@ namespace Barotrauma
return node;
}
private struct MinimapNodeData
public struct MinimapNodeData
{
public Order Order;
}

View File

@@ -761,6 +761,7 @@ namespace Barotrauma
item.PendingItemSwap = null;
}
}
campaign.CampaignUI?.UpgradeStore?.RequestRefresh();
}
}

View File

@@ -78,7 +78,7 @@ namespace Barotrauma.Tutorials
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)
{
@@ -119,6 +119,12 @@ namespace Barotrauma.Tutorials
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
SegmentType = TutorialSegmentType.Objective;
}
public void ConnectMessageBox(Segment messageBoxSegment)
{
SegmentType = TutorialSegmentType.MessageBox;
OnClickObjective = messageBoxSegment.OnClickObjective;
}
}
private bool completed;
@@ -144,6 +150,7 @@ namespace Barotrauma.Tutorials
private readonly EventPrefab eventPrefab;
private CoroutineHandle tutorialCoroutine;
private CoroutineHandle completedCoroutine;
private Character character;
@@ -154,7 +161,7 @@ namespace Barotrauma.Tutorials
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
@@ -331,13 +338,16 @@ namespace Barotrauma.Tutorials
{
CoroutineManager.StopCoroutines(tutorialCoroutine);
}
GUI.PreventPauseMenuToggle = false;
if (completedCoroutine == null && !CoroutineManager.IsCoroutineRunning(completedCoroutine))
{
GUI.PreventPauseMenuToggle = false;
}
ContentRunning = false;
infoBox = null;
}
else if (Character.Controlled.IsDead)
else
{
CoroutineManager.StartCoroutine(Dead());
character = Character.Controlled;
}
}
}
@@ -385,7 +395,7 @@ namespace Barotrauma.Tutorials
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;
}
@@ -399,7 +409,7 @@ namespace Barotrauma.Tutorials
}
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;
}
@@ -409,10 +419,12 @@ namespace Barotrauma.Tutorials
public void Complete()
{
GameAnalyticsManager.AddDesignEvent($"Tutorial:{Identifier}:Completed");
CoroutineManager.StartCoroutine(TutorialCompleted());
completedCoroutine = CoroutineManager.StartCoroutine(TutorialCompleted());
IEnumerable<CoroutineStatus> TutorialCompleted()
{
while (GUI.PauseMenuOpen) { yield return CoroutineStatus.Running; }
GUI.PreventPauseMenuToggle = true;
Character.Controlled.ClearInputs();
Character.Controlled = null;
@@ -423,7 +435,7 @@ namespace Barotrauma.Tutorials
var endCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: FadeOutTime);
Completed = true;
while (endCinematic.Running) { yield return null; }
while (endCinematic.Running) { yield return CoroutineStatus.Running; }
Stop();
GameMain.MainMenuScreen.ReturnToMainMenu(null, null);
@@ -432,16 +444,18 @@ namespace Barotrauma.Tutorials
private bool Restart(GUIButton button, object obj)
{
GUI.PreventPauseMenuToggle = false;
GUIMessageBox.MessageBoxes.Clear();
GameMain.MainMenuScreen.ReturnToMainMenu(button, obj);
Start();
return true;
}
public void TriggerTutorialSegment(Segment segment)
public void TriggerTutorialSegment(Segment segment, bool connectObjective = false)
{
if (segment.SegmentType != TutorialSegmentType.InfoBox)
{
ActiveObjectives.Add(segment);
AddToObjectiveList(segment);
AddToObjectiveList(segment, connectObjective);
return;
}
@@ -488,11 +502,14 @@ namespace Barotrauma.Tutorials
}
if (GUIStyle.GetComponentStyle("ObjectiveIndicatorCompleted") is GUIComponentStyle style)
{
//return if already completed
if (segment.ObjectiveStateIndicator.Style == style) { return; }
segment.ObjectiveStateIndicator.ApplyStyle(style);
}
segment.ObjectiveStateIndicator.Parent.Flash(color: GUIStyle.Green, flashDuration: 0.35f, useRectangleFlash: true);
segment.ObjectiveButton.OnClicked = null;
segment.ObjectiveButton.CanBeFocused = false;
GameAnalyticsManager.AddDesignEvent($"Tutorial:{Identifier}:{segmentId}:Completed");
}
public void RemoveTutorialSegment(Identifier segmentId)
@@ -568,8 +585,18 @@ namespace Barotrauma.Tutorials
/// <summary>
/// Adds the segment to the objective list
/// </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)
{
AbsoluteOffset = GetObjectiveHiddenPosition(),
@@ -595,15 +622,25 @@ namespace Barotrauma.Tutorials
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);
segment.ObjectiveButton = new GUIButton(new RectTransform(Vector2.One, segment.LinkedTextBlock.RectTransform, Anchor.TopLeft, Pivot.TopLeft), style: null)
{
CanBeFocused = segment.SegmentType != TutorialSegmentType.Objective,
ToolTip = objectiveTextTranslated,
OnClicked = (GUIButton btn, object userdata) =>
ToolTip = objectiveTextTranslated
};
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)
{
@@ -621,13 +658,8 @@ namespace Barotrauma.Tutorials
segment.OnClickObjective?.Invoke();
}
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)

View File

@@ -4,6 +4,7 @@ using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
namespace Barotrauma.Items.Components
{
@@ -177,7 +178,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
Color color = item.SpriteColor;
Color color = item.GetSpriteColor(withHighlight: true);
if (brokenSprite == null)
{
//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);
}
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; }
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.MaxCondition : 1.0f;
float alpha = fadeBrokenSprite ? 1.0f - healthRatio : 1.0f;
spriteBatch.Draw(brokenSprite.Texture, pos,
getSourceRect(brokenSprite, openState, IsHorizontal),
color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, item.SpriteEffects,

View File

@@ -580,7 +580,7 @@ namespace Barotrauma.Items.Components
GameMain.Instance.ResolutionChanged += OnResolutionChangedPrivate;
}
protected void TryCreateDragHandle()
protected virtual void TryCreateDragHandle()
{
if (GuiFrame != null && GuiFrameSource.GetAttributeBool("draggable", true))
{
@@ -593,6 +593,7 @@ namespace Barotrauma.Items.Components
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) },
style: "GUIDragIndicatorHorizontal");
dragIcon.RectTransform.MinSize = new Point(0, iconHeight);
handle.ValidatePosition = (RectTransform rectT) =>
{
@@ -622,7 +623,7 @@ namespace Barotrauma.Items.Components
};
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")
{
OnClicked = (btn, userdata) =>

View File

@@ -324,6 +324,8 @@ namespace Barotrauma.Items.Components
int i = 0;
foreach (Item containedItem in Inventory.AllItems)
{
if (containedItem?.Sprite == null) { continue; }
if (AutoInteractWithContained)
{
containedItem.IsHighlighted = item.IsHighlighted;
@@ -344,7 +346,7 @@ namespace Barotrauma.Items.Components
containedItem.Sprite.Draw(
spriteBatch,
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,
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation ),
containedItem.Scale,

View File

@@ -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);
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 },
OnClicked = OnActivateButtonClicked
};
@@ -432,17 +433,22 @@ namespace Barotrauma.Items.Components
private bool OnActivateButtonClicked(GUIButton button, object obj)
{
var disallowedItem = inputContainer.Inventory.FindItem(i => !i.AllowDeconstruct, recursive: false);
if (disallowedItem != null && !DeconstructItemsSimultaneously)
if (!IsActive)
{
int index = inputContainer.Inventory.FindIndex(disallowedItem);
if (index >= 0 && index < inputContainer.Inventory.visualSlots.Length)
//don't allow turning on if there's non-deconstructable items in the queue
var disallowedItem = inputContainer.Inventory.FindItem(i => !i.AllowDeconstruct, recursive: false);
if (disallowedItem != null && !DeconstructItemsSimultaneously)
{
var slot = inputContainer.Inventory.visualSlots[index];
slot?.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f);
int index = inputContainer.Inventory.FindIndex(disallowedItem);
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)
{
pendingState = !IsActive;

View File

@@ -32,6 +32,8 @@ namespace Barotrauma.Items.Components
}
private FabricationRecipe selectedItem;
public Identifier SelectedItemIdentifier => SelectedItem?.TargetItem.Identifier ?? Identifier.Empty;
private GUIComponent inSufficientPowerWarning;
private FabricationRecipe pendingFabricatedItem;

View File

@@ -1013,12 +1013,14 @@ namespace Barotrauma.Items.Components
}
else if (hullData.LinkedHulls.Any())
{
hullData.HullWaterAmount = 0.0f;
float waterVolume = 0.0f;
float totalVolume = 0.0f;
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
{

View File

@@ -1,10 +1,8 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -58,6 +56,7 @@ namespace Barotrauma.Items.Components
RelativeOffset = new Vector2(0, 0.1f)
}, style: "PowerButton")
{
UserData = UIHighlightAction.ElementId.PowerButton,
OnClicked = (button, data) =>
{
TargetLevel = null;
@@ -114,6 +113,7 @@ namespace Barotrauma.Items.Components
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 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);

View File

@@ -234,6 +234,7 @@ namespace Barotrauma.Items.Components
return false;
}
};
FissionRateScrollBar.Frame.UserData = UIHighlightAction.ElementId.FissionRateSlider;
TurbineOutputScrollBar = new GUIScrollBar(new RectTransform(sliderSize, rightArea.RectTransform, Anchor.TopCenter)
{
@@ -252,6 +253,7 @@ namespace Barotrauma.Items.Components
return false;
}
};
TurbineOutputScrollBar.Frame.UserData = UIHighlightAction.ElementId.TurbineOutputSlider;
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.2f), columnLeft.RectTransform))
{
@@ -302,9 +304,10 @@ namespace Barotrauma.Items.Components
new GUIFrame(new RectTransform(new Vector2(0.01f, 1.0f), topRightArea.RectTransform), style: "VerticalLine");
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")
{
UserData = UIHighlightAction.ElementId.AutoTempSwitch,
Enabled = false,
Selected = AutoTemp,
ClickSound = GUISoundType.UISwitch,
@@ -347,6 +350,7 @@ namespace Barotrauma.Items.Components
RelativeOffset = new Vector2(0, 0.1f)
}, style: "PowerButton")
{
UserData = UIHighlightAction.ElementId.PowerButton,
OnClicked = (button, data) =>
{
PowerOn = !PowerOn;

View File

@@ -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);
SonarModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), sonarModeArea.RectTransform), string.Empty, style: "SwitchVertical")
{
UserData = UIHighlightAction.ElementId.SonarModeSwitch,
Selected = false,
Enabled = true,
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),
TextManager.Get("SonarPassive"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRedSmall")
{
UserData = UIHighlightAction.ElementId.PassiveSonarIndicator,
ToolTip = TextManager.Get("SonarTipPassive"),
Selected = true,
Enabled = false
@@ -245,6 +247,7 @@ namespace Barotrauma.Items.Components
activeTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), sonarModeRightSide.RectTransform, Anchor.BottomLeft),
TextManager.Get("SonarActive"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRedSmall")
{
UserData = UIHighlightAction.ElementId.ActiveSonarIndicator,
ToolTip = TextManager.Get("SonarTipActive"),
Selected = 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")
{ 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")
{
OnClicked = (button, data) =>
@@ -334,6 +342,18 @@ namespace Barotrauma.Items.Components
sonarView.RectTransform.RelativeOffset = new Vector2(0.13f * GUI.RelativeHorizontalAspectRatio, 0);
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)

View File

@@ -1,13 +1,11 @@
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.Extensions;
using System.Linq;
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);
steeringModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), steeringModeArea.RectTransform), string.Empty, style: "SwitchVertical")
{
UserData = UIHighlightAction.ElementId.SteeringModeSwitch,
Selected = autoPilot,
Enabled = true,
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),
TextManager.Get("SteeringMaintainPos"), font: GUIStyle.SmallFont, style: "GUIRadioButton")
{
UserData = UIHighlightAction.ElementId.MaintainPosTickBox,
Enabled = autoPilot,
Selected = maintainPos,
OnSelected = tickBox =>

View File

@@ -83,7 +83,8 @@ namespace Barotrauma.Items.Components
}
};
rechargeSpeedSlider.Bar.RectTransform.MaxSize = new Point(rechargeSpeedSlider.Bar.Rect.Height);
rechargeSpeedSlider.Frame.UserData = UIHighlightAction.ElementId.RechargeSpeedSlider;
// lower area --------------------------
var chargeTextContainer = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), lowerArea.RectTransform), style: null);

View File

@@ -165,6 +165,7 @@ namespace Barotrauma.Items.Components
repairingText = TextManager.Get("Repairing");
RepairButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), progressBarHolder.RectTransform, Anchor.TopCenter), repairButtonText)
{
UserData = UIHighlightAction.ElementId.RepairButton,
OnClicked = (btn, obj) =>
{
requestStartFixAction = FixActions.Repair;

View File

@@ -135,7 +135,7 @@ namespace Barotrauma.Items.Components
}
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)
{

View File

@@ -118,7 +118,7 @@ namespace Barotrauma
return GetDrawDepth(SpriteDepth + DrawDepthOffset, Sprite);
}
public Color GetSpriteColor()
public Color GetSpriteColor(bool withHighlight = false)
{
Color color = spriteColor;
if (Prefab.UseContainedSpriteColor && ownInventory != null)
@@ -129,6 +129,17 @@ namespace Barotrauma
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;
}
@@ -281,9 +292,7 @@ namespace Barotrauma
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();
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
Color color = IsIncludedInSelection && editing ? GUIStyle.Blue : GetSpriteColor(withHighlight: true);
bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null;
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;

View File

@@ -229,6 +229,7 @@ namespace Barotrauma
Submarine = Submarine.MainSub
};
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.Submarine = Submarine.MainSub;

View File

@@ -264,10 +264,6 @@ namespace Barotrauma
{
Vector2 velocity = flowForce;
if (!IsHorizontal)
{
velocity.X = Rand.Range(-100.0f, 100.0f) * open;
}
else
{
velocity.X *= Rand.Range(1.0f, 3.0f);
}

View File

@@ -2,7 +2,6 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Networking
{
@@ -88,12 +87,13 @@ namespace Barotrauma.Networking
TextManager.Get("BanPermanent") : TextManager.GetWithVariable("BanExpires", "[time]", bannedPlayer.ExpirationTime.Value.ToString()),
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),
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)
{
ToolTip = bannedPlayer.Reason
ToolTip = reason
};
paddedPlayerFrame.Recalculate();

View File

@@ -31,12 +31,17 @@ namespace Barotrauma.Networking
public bool IsOwner;
public bool AllowKicking;
public bool IsDownloading;
public float Karma;
public bool AllowKicking =>
!IsOwner &&
!HasPermission(ClientPermissions.Ban) &&
!HasPermission(ClientPermissions.Kick) &&
!HasPermission(ClientPermissions.Unban);
public void UpdateSoundPosition()
{
if (VoipSound == null) { return; }

View File

@@ -362,8 +362,7 @@ namespace Barotrauma.Networking
// When this is set to true, we are approved and ready to go
canStart = false;
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 40);
DateTime reqAuthTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, 200);
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 200);
// Loop until we are approved
LocalizedString connectingText = TextManager.Get("Connecting");
@@ -489,7 +488,10 @@ namespace Barotrauma.Networking
}
GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
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();
GameMain.ServerListScreen.Select();
return;
@@ -965,10 +967,10 @@ namespace Barotrauma.Networking
}
AttemptReconnect(disconnectPacket);
}
else if (disconnectPacket.ShouldShowMessage)
else
{
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
};
@@ -1033,6 +1035,7 @@ namespace Barotrauma.Networking
if (ClientPeer != null)
{
//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;
}
}
@@ -1721,6 +1724,7 @@ namespace Barotrauma.Networking
string subName = inc.ReadString();
string subHash = inc.ReadString();
byte subClass = inc.ReadByte();
bool isShuttle = inc.ReadBoolean();
bool requiredContentPackagesInstalled = inc.ReadBoolean();
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.StringRepresentation == subHash);
@@ -1730,6 +1734,7 @@ namespace Barotrauma.Networking
{
SubmarineClass = (SubmarineClass)subClass
};
if (isShuttle) { matchingSub.AddTag(SubmarineTag.Shuttle); }
}
matchingSub.RequiredContentPackagesInstalled = requiredContentPackagesInstalled;
ServerSubmarines.Add(matchingSub);
@@ -1780,7 +1785,6 @@ namespace Barotrauma.Networking
AccountInfo = tc.AccountInfo,
Muted = tc.Muted,
InGame = tc.InGame,
AllowKicking = tc.AllowKicking,
IsOwner = tc.IsOwner
};
otherClients.Add(existingClient);
@@ -1795,7 +1799,6 @@ namespace Barotrauma.Networking
existingClient.Muted = tc.Muted;
existingClient.InGame = tc.InGame;
existingClient.IsOwner = tc.IsOwner;
existingClient.AllowKicking = tc.AllowKicking;
existingClient.IsDownloading = tc.IsDownloading;
GameMain.NetLobbyScreen.SetPlayerNameAndJobPreference(existingClient);
if (Screen.Selected != GameMain.NetLobbyScreen && tc.CharacterId > 0)
@@ -2404,7 +2407,7 @@ namespace Barotrauma.Networking
var subElement = subListChildren.FirstOrDefault(c =>
((SubmarineInfo)c.UserData).Name == newSub.Name &&
((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);
subElement.GetChild<GUITextBlock>().TextColor = newSubTextColor;
@@ -2429,7 +2432,7 @@ namespace Barotrauma.Networking
if (GameMain.NetLobbyScreen.FailedSelectedShuttle.HasValue &&
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);
}
@@ -2583,6 +2586,7 @@ namespace Barotrauma.Networking
}
}
ChildServerRelay.ShutDown();
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
characterInfo?.Remove();

View File

@@ -49,7 +49,7 @@ namespace Barotrauma.Networking
protected abstract void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body);
protected ConnectionInitialization initializationStep;
public bool ContentPackageOrderReceived { get; protected set; }
public bool ContentPackageOrderReceived { get; set; }
protected int passwordSalt;
protected Steamworks.AuthTicket? steamAuthTicket;
private GUIMessageBox? passwordMsgBox;

View File

@@ -36,6 +36,8 @@ namespace Barotrauma.Networking
public override void Start()
{
if (isActive) { return; }
ContentPackageOrderReceived = false;
steamAuthTicket = SteamManager.GetAuthSessionTicket();
@@ -189,6 +191,7 @@ namespace Barotrauma.Networking
if (packet is { SteamId: var steamId, Data: var data })
{
OnP2PData(steamId, data, data.Length);
if (!isActive) { return; }
receivedBytes += data.Length;
}
}

View File

@@ -391,7 +391,7 @@ namespace Barotrauma.Networking
for (int i = remotePeers.Count - 1; i >= 0; i--)
{
DisconnectPeer(remotePeers[i], peerDisconnectPacket);
DisconnectPeer(remotePeers[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
Thread.Sleep(100);

View File

@@ -37,6 +37,7 @@ namespace Barotrauma.Networking
switch (serverInfo.Endpoint)
{
case LidgrenEndpoint { NetEndpoint: { Address: var address } }:
GetIPAddressPing(serverInfo, address, onPingDiscovered);
break;
case SteamP2PEndpoint steamP2PEndpoint:
@@ -132,24 +133,30 @@ namespace Barotrauma.Networking
private static void GetIPAddressPing(ServerInfo serverInfo, IPAddress address, Action<ServerInfo> onPingDiscovered)
{
lock (activePings)
if (IPAddress.IsLoopback(address))
{
if (activePings.ContainsKey(address)) { return; }
activePings.Add(address, activePings.Any() ? activePings.Values.Max() + 1 : 0);
serverInfo.Ping = Option<int>.Some(0);
onPingDiscovered(serverInfo);
}
serverInfo.Ping = Option<int>.None();
TaskPool.Add($"PingServerAsync ({address})", PingServerAsync(address, 1000),
rtt =>
else
{
lock (activePings)
{
if (!rtt.TryGetResult(out serverInfo.Ping)) { serverInfo.Ping = Option<int>.None(); }
onPingDiscovered(serverInfo);
lock (activePings)
if (activePings.ContainsKey(address)) { return; }
activePings.Add(address, activePings.Any() ? activePings.Values.Max() + 1 : 0);
}
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)

View File

@@ -70,7 +70,7 @@ namespace Barotrauma.Networking
[Serialize(PlayStyle.Casual, IsPropertySaveable.Yes)]
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();
@@ -200,7 +200,7 @@ namespace Barotrauma.Networking
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"),
GameVersion.ToString()))
GameVersion == new Version(0, 0, 0, 0) ? TextManager.Get("Unknown") : GameVersion.ToString()))
{
CanBeFocused = false
};
@@ -397,10 +397,10 @@ namespace Barotrauma.Networking
public void UpdateInfo(Func<string, string?> valueGetter)
{
ServerMessage = valueGetter("message") ?? "";
GameVersion = Version.TryParse(valueGetter("version"), out var version)
? version
: GameMain.Version;
if (Version.TryParse(valueGetter("version"), out var version))
{
GameVersion = version;
}
if (int.TryParse(valueGetter("playercount"), out int playerCount)) { PlayerCount = playerCount; }
if (int.TryParse(valueGetter("maxplayernum"), out int maxPlayers)) { MaxPlayers = maxPlayers; }
if (Enum.TryParse(valueGetter("modeselectionmode"), out SelectionMode modeSelectionMode)) { ModeSelectionMode = modeSelectionMode; }

View File

@@ -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);
// missionName.RectTransform.MinSize = new Point(0, (int)(missionName.Rect.Height * 1.5f));
missionName.RectTransform.MinSize = new Point(0, GUI.IntScale(15));
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)

View File

@@ -67,7 +67,9 @@ namespace Barotrauma
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
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 });
//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();
CreateTutorialTab();
this.game = game;
@@ -459,24 +448,68 @@ namespace Barotrauma
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))
{
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
};
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()
{
if (GameMain.MainMenuScreen is not MainMenuScreen menuScreen) { return; }
menuScreen.tutorialList.ClearChildren();
menuScreen.CreateTutorialButtons();
menuScreen.CreateTutorialTab();
}
#endregion
@@ -746,12 +779,13 @@ namespace Barotrauma
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;
tutorialText.Text = CompletedTutorials.Instance.Contains(tutorial.Identifier) ?
TextManager.GetWithVariable("tutorialcompleted", "[tutorialname]", tutorial.DisplayName) :
tutorial.DisplayName;
if (CompletedTutorials.Instance.Contains(tutorial.Identifier) && tutorialText.GetChild<GUIImage>() == null)
{
new GUIImage(new RectTransform(new Point((int)(tutorialText.Padding.X * 0.8f)), tutorialText.RectTransform, Anchor.CenterLeft), style: "ObjectiveIndicatorCompleted");
}
}
}

View File

@@ -1862,7 +1862,7 @@ namespace Barotrauma
SubmarineType.Wreck => "Content/Map/Wrecks/{0}",
SubmarineType.BeaconStation => "Content/Map/BeaconStations/{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()
}, savePath);
modProject.ModVersion = "";

View File

@@ -195,13 +195,13 @@ namespace Barotrauma.Sounds
GainMultipliers[index] = gain;
}
}
private Dictionary<string, CategoryModifier> categoryModifiers;
private readonly Dictionary<string, CategoryModifier> categoryModifiers = new Dictionary<string, CategoryModifier>();
public SoundManager()
{
loadedSounds = new List<Sound>();
streamingThread = null;
categoryModifiers = null;
sourcePools = new SoundSourcePool[2];
playingChannels[(int)SourcePoolIndex.Default] = new SoundChannel[SOURCE_COUNT];
@@ -235,7 +235,7 @@ namespace Barotrauma.Sounds
CompressionDynamicRangeGain = 1.0f;
}
private void SetAudioOutputDevice(string deviceName)
private static void SetAudioOutputDevice(string deviceName)
{
var config = GameSettings.CurrentConfig;
config.Audio.AudioOutputDevice = deviceName;
@@ -249,7 +249,7 @@ namespace Barotrauma.Sounds
DebugConsole.NewMessage($"Attempting to open ALC device \"{deviceName}\"");
alcDevice = IntPtr.Zero;
int alcError = Al.NoError;
int alcError;
for (int i = 0; i < 3; i++)
{
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")}\".");
}
var newSound = new OggSound(this, filePath, stream, xElement: element);
newSound.BaseGain = element.GetAttributeFloat("volume", 1.0f);
var newSound = new OggSound(this, filePath, stream, xElement: element)
{
BaseGain = element.GetAttributeFloat("volume", 1.0f)
};
float range = element.GetAttributeFloat("range", 1000.0f);
newSound.BaseNear = range * 0.4f;
newSound.BaseFar = range;
@@ -537,14 +539,16 @@ namespace Barotrauma.Sounds
{
if (Disabled) { return; }
category = category.ToLower();
if (categoryModifiers == null) categoryModifiers = new Dictionary<string, CategoryModifier>();
if (!categoryModifiers.ContainsKey(category))
lock (categoryModifiers)
{
categoryModifiers.Add(category, new CategoryModifier(index, gain, false));
}
else
{
categoryModifiers[category].SetGainMultiplier(index, gain);
if (!categoryModifiers.ContainsKey(category))
{
categoryModifiers.Add(category, new CategoryModifier(index, gain, false));
}
else
{
categoryModifiers[category].SetGainMultiplier(index, gain);
}
}
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; }
category = category.ToLower();
if (categoryModifiers == null || !categoryModifiers.ContainsKey(category)) return 1.0f;
if (index < 0)
lock (categoryModifiers)
{
float accumulatedMultipliers = 1.0f;
for (int i = 0; i < categoryModifiers[category].GainMultipliers.Length; i++)
if (categoryModifiers == null || !categoryModifiers.TryGetValue(category, out CategoryModifier categoryModifier)) { return 1.0f; }
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; }
category = category.ToLower();
if (categoryModifiers == null) { categoryModifiers = new Dictionary<string, CategoryModifier>(); }
if (!categoryModifiers.ContainsKey(category))
lock (categoryModifiers)
{
categoryModifiers.Add(category, new CategoryModifier(0, 1.0f, muffle));
}
else
{
categoryModifiers[category].Muffle = muffle;
if (!categoryModifiers.ContainsKey(category))
{
categoryModifiers.Add(category, new CategoryModifier(0, 1.0f, muffle));
}
else
{
categoryModifiers[category].Muffle = muffle;
}
}
for (int i = 0; i < playingChannels.Length; i++)
@@ -618,8 +626,11 @@ namespace Barotrauma.Sounds
if (Disabled) { return false; }
category = category.ToLower();
if (categoryModifiers == null || !categoryModifiers.ContainsKey(category)) { return false; }
return categoryModifiers[category].Muffle;
lock (categoryModifiers)
{
if (categoryModifiers == null || !categoryModifiers.TryGetValue(category, out CategoryModifier categoryModifier)) { return false; }
return categoryModifier.Muffle;
}
}
public void Update()

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.19.5.0</Version>
<Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.19.5.0</Version>
<Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.19.5.0</Version>
<Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.19.5.0</Version>
<Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.19.5.0</Version>
<Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -512,13 +512,10 @@ namespace Barotrauma
msg.WriteByte((byte)TeamID);
break;
case AddToCrewEventData addToCrewEventData:
msg.WriteByte((byte)addToCrewEventData.TeamType); // team id
ushort[] inventoryItemIDs = addToCrewEventData.InventoryItems.Select(item => item.ID).ToArray();
msg.WriteUInt16((ushort)inventoryItemIDs.Length);
for (int i = 0; i < inventoryItemIDs.Length; i++)
{
msg.WriteUInt16(inventoryItemIDs[i]);
}
msg.WriteNetSerializableStruct(addToCrewEventData.ItemTeamChange);
break;
case RemoveFromCrewEventData removeFromCrewEventData:
msg.WriteNetSerializableStruct(removeFromCrewEventData.ItemTeamChange);
break;
case UpdateExperienceEventData _:
msg.WriteInt32(Info.ExperiencePoints);

View File

@@ -1613,6 +1613,7 @@ namespace Barotrauma.Networking
outmsg.WriteString(sub.Name);
outmsg.WriteString(sub.MD5Hash.ToString());
outmsg.WriteByte((byte)sub.SubmarineClass);
outmsg.WriteBoolean(sub.HasTag(SubmarineTag.Shuttle));
outmsg.WriteBoolean(sub.RequiredContentPackagesInstalled);
}
@@ -1836,10 +1837,6 @@ namespace Barotrauma.Networking
InGame = client.InGame,
HasPermissions = client.Permissions != ClientPermissions.None,
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)
};
@@ -3383,7 +3380,7 @@ namespace Barotrauma.Networking
private IEnumerable<CoroutineStatus> SendClientPermissionsAfterClientListSynced(Client recipient, Client client)
{
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))
{

View File

@@ -225,7 +225,7 @@ namespace Barotrauma.Networking
}
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)
{
@@ -373,7 +373,7 @@ namespace Barotrauma.Networking
{
if (netServer == null) { return; }
if (!(conn is LidgrenConnection lidgrenConn)) { return; }
if (conn is not LidgrenConnection lidgrenConn) { return; }
if (connectedClients.Contains(lidgrenConn))
{
@@ -432,7 +432,7 @@ namespace Barotrauma.Networking
}
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)
{

View File

@@ -167,7 +167,14 @@ namespace Barotrauma.Networking
if (pendingClient.AccountInfo.AccountId.TryUnwrap(out var id)) { banAccountId(id); }
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)

View File

@@ -1,8 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
namespace Barotrauma.Networking
{
@@ -139,7 +137,27 @@ namespace Barotrauma.Networking
pendingClient?.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))
{
if (pendingClient != null)
@@ -167,24 +185,6 @@ namespace Barotrauma.Networking
//message exists solely as a heartbeat, ignore its contents
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)
{
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
@@ -248,7 +248,7 @@ namespace Barotrauma.Networking
{
if (!started) { return; }
if (!(conn is SteamP2PConnection steamP2PConn)) { return; }
if (conn is not SteamP2PConnection steamP2PConn) { return; }
if (!connectedClients.Contains(steamP2PConn) && conn != OwnerConnection)
{
@@ -256,7 +256,7 @@ namespace Barotrauma.Networking
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 _);
@@ -292,9 +292,9 @@ namespace Barotrauma.Networking
{
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);

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.19.5.0</Version>
<Version>0.19.8.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -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));
if (steeringManager == insideSteering)

View File

@@ -913,11 +913,14 @@ namespace Barotrauma
private void RemoveFollowTarget()
{
if (arrestingRegistered)
if (followTargetObjective != null)
{
followTargetObjective.Completed -= OnArrestTargetReached;
if (arrestingRegistered)
{
followTargetObjective.Completed -= OnArrestTargetReached;
}
RemoveSubObjective(ref followTargetObjective);
}
RemoveSubObjective(ref followTargetObjective);
arrestingRegistered = false;
}

View File

@@ -595,6 +595,10 @@ namespace Barotrauma
{
return c.CurrentHull;
}
else if (target is Structure structure)
{
return Hull.FindHull(structure.Position, useWorldCoordinates: false);
}
else if (target is Gap g)
{
return g.FlowTargetHull;

View File

@@ -54,7 +54,7 @@ namespace Barotrauma
if (ValidContainableItemIdentifiers.None())
{
#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
Abandon = true;
return;
@@ -250,7 +250,7 @@ namespace Barotrauma
catch (NotImplementedException)
{
#if DEBUG
DebugConsole.ShowError($"Unexpected target condition \"{TargetItemCondition}\" in local function GetConditionBasedProperty");
DebugConsole.LogError($"Unexpected target condition \"{TargetItemCondition}\" in local function GetConditionBasedProperty");
#endif
return 0.0f;
}

View File

@@ -90,7 +90,7 @@ namespace Barotrauma
catch (NotImplementedException)
{
#if DEBUG
DebugConsole.ShowError($"Unexpected target condition \"{targetCondition}\" in AIObjectiveLoadItems.ItemMatchesTargetCondition");
DebugConsole.LogError($"Unexpected target condition \"{targetCondition}\" in AIObjectiveLoadItems.ItemMatchesTargetCondition");
#endif
return false;
}

View File

@@ -37,6 +37,8 @@ namespace Barotrauma
public Func<bool> completionCondition;
private bool isDoneOperating;
public float? OverridePriority = null;
protected override float GetPriority()
{
bool isOrder = objectiveManager.IsOrder(this);
@@ -52,7 +54,11 @@ namespace Barotrauma
}
else
{
if (isOrder)
if (OverridePriority.HasValue)
{
Priority = OverridePriority.Value;
}
else if (isOrder)
{
Priority = objectiveManager.GetOrderPriority(this);
}
@@ -135,7 +141,7 @@ namespace Barotrauma
float value = CumulatedDevotion + (max * PriorityModifier);
Priority = MathHelper.Clamp(value, 0, max);
}
else
else if (!OverridePriority.HasValue)
{
float value = CumulatedDevotion + (AIObjectiveManager.LowestOrderPriority * PriorityModifier);
float max = AIObjectiveManager.LowestOrderPriority - 1;
@@ -204,8 +210,15 @@ namespace Barotrauma
{
if (!character.IsClimbing && character.CanInteractWith(target.Item, out _, checkLinked: false))
{
HumanAIController.FaceTarget(target.Item);
if (character.SelectedItem != target.Item)
if (target.Item.GetComponent<Controller>() is not Controller { ControlCharacterPose: true })
{
HumanAIController.FaceTarget(target.Item);
}
else
{
HumanAIController.SteeringManager.Reset();
}
if (character.SelectedItem != target.Item && character.SelectedSecondaryItem != target.Item)
{
target.Item.TryInteract(character, forceSelectKey: true);
}

View File

@@ -278,7 +278,7 @@ namespace Barotrauma
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
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
foreach (Affliction affliction in GetSortedAfflictions(targetCharacter))

View File

@@ -3,9 +3,8 @@ using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;
using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma
{
@@ -151,7 +150,7 @@ namespace Barotrauma
public OrderPrefab(ContentXElement orderElement, OrdersFile file) : base(file, orderElement.GetAttributeIdentifier("identifier", ""))
{
Name = TextManager.Get($"OrderName.{Identifier}");
ContextualName = TextManager.Get($"OrderNameContextual.{Identifier}");
ContextualName = TextManager.Get($"OrderNameContextual.{Identifier}").Fallback(Name);
string targetItemType = orderElement.GetAttributeString("targetitemtype", "");
if (!string.IsNullOrWhiteSpace(targetItemType))
@@ -435,7 +434,7 @@ namespace Barotrauma
}
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;
}
}

View File

@@ -336,7 +336,7 @@ namespace Barotrauma
{
ApplyTestPose();
}
else
else if (character.SelectedBy == null)
{
if (character.LockHands)
{
@@ -356,7 +356,7 @@ namespace Barotrauma
HandIK(rightHand, 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)
{
@@ -1716,9 +1716,17 @@ namespace Barotrauma
}
else if (target is AICharacter && target != Character.Controlled)
{
target.AnimController.TargetDir = WorldPosition.X > target.WorldPosition.X ? Direction.Right : Direction.Left;
Vector2 movement = (character.SimPosition + Vector2.UnitX * 0.5f * Dir) - target.SimPosition;
target.AnimController.TargetMovement = movement.LengthSquared() > 0.01f ? movement : Vector2.Zero;
if (target.AnimController.Dir > 0 == WorldPosition.X > target.WorldPosition.X)
{
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;
}
}
}

View File

@@ -402,9 +402,8 @@ namespace Barotrauma
}
else
{
string afflictionIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();
afflictionPrefab = AfflictionPrefab.Prefabs[afflictionIdentifier];
if (afflictionPrefab == null)
Identifier afflictionIdentifier = subElement.GetAttributeIdentifier("identifier", "");
if (!AfflictionPrefab.Prefabs.TryGet(afflictionIdentifier, out afflictionPrefab))
{
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionIdentifier + "\" not found.");
continue;
@@ -430,15 +429,13 @@ namespace Barotrauma
Afflictions.Clear();
foreach (var subElement in element.GetChildElements("affliction"))
{
AfflictionPrefab afflictionPrefab;
Affliction affliction;
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}\".");
continue;
}
afflictionPrefab = AfflictionPrefab.Prefabs[afflictionIdentifier];
affliction = afflictionPrefab.Instantiate(0.0f);
affliction.Deserialize(subElement);
//backwards compatibility

View File

@@ -549,6 +549,10 @@ namespace Barotrauma
set
{
lockHandsTimer = MathHelper.Clamp(lockHandsTimer + (value ? 1.0f : -0.5f), 0.0f, 10.0f);
if (value)
{
SelectedCharacter = null;
}
#if CLIENT
HintManager.OnHandcuffed(this);
#endif
@@ -599,13 +603,10 @@ namespace Barotrauma
get { return selectedCharacter; }
set
{
if (value == selectedCharacter) return;
if (selectedCharacter != null)
selectedCharacter.selectedBy = null;
if (value == selectedCharacter) { return; }
if (selectedCharacter != null) { selectedCharacter.selectedBy = null; }
selectedCharacter = value;
if (selectedCharacter != null)
selectedCharacter.selectedBy = this;
if (selectedCharacter != null) {selectedCharacter.selectedBy = this; }
#if CLIENT
CharacterHealth.SetHealthBarVisibility(value == null);
#endif
@@ -707,6 +708,14 @@ namespace Barotrauma
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 float KnockbackCooldownTimer;
@@ -2620,7 +2629,7 @@ namespace Barotrauma
if (!AllowInput)
{
FocusedCharacter = null;
if (SelectedCharacter != null) DeselectCharacter();
if (SelectedCharacter != null) { DeselectCharacter(); }
return;
}
}
@@ -3636,7 +3645,7 @@ namespace Barotrauma
string modifiedMessage = ChatMessage.ApplyDistanceEffect(message.Message, message.MessageType.Value, this, Controlled);
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
@@ -4020,6 +4029,7 @@ namespace Barotrauma
if (newStun > 0.0f)
{
SelectedItem = SelectedSecondaryItem = null;
if (SelectedCharacter != null) { DeselectCharacter(); }
}
HealthUpdateInterval = 0.0f;
}

View File

@@ -1,8 +1,9 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma
{
@@ -25,9 +26,10 @@ namespace Barotrauma
UpdateSkills = 12,
UpdateMoney = 13,
UpdatePermanentStats = 14,
RemoveFromCrew = 15,
MinValue = 0,
MaxValue = 14
MaxValue = 15
}
private interface IEventData : NetEntityEvent.IData
@@ -133,18 +135,30 @@ namespace Barotrauma
public EventType EventType => EventType.TeamChange;
}
[NetworkSerialize]
public readonly record struct ItemTeamChange(CharacterTeamType TeamId, ImmutableArray<UInt16> ItemIds) : INetSerializableStruct;
public struct AddToCrewEventData : IEventData
{
public EventType EventType => EventType.AddToCrew;
public readonly CharacterTeamType TeamType;
public readonly ImmutableArray<Item> InventoryItems;
public readonly ItemTeamChange ItemTeamChange;
public AddToCrewEventData(CharacterTeamType teamType, IEnumerable<Item> inventoryItems)
{
TeamType = teamType;
InventoryItems = inventoryItems.ToImmutableArray();
ItemTeamChange = new ItemTeamChange(teamType, inventoryItems.Select(it => it.ID).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

View File

@@ -82,10 +82,10 @@ namespace Barotrauma
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 List<CharacterStateInfo> memLocalState = new List<CharacterStateInfo>();
private readonly List<CharacterStateInfo> memState = new List<CharacterStateInfo>();
private readonly List<CharacterStateInfo> memLocalState = new List<CharacterStateInfo>();
public float healthUpdateTimer;

View File

@@ -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="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>
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
//float = suitability
@@ -1062,7 +1062,18 @@ namespace Barotrauma
}
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)
{
@@ -1153,10 +1164,10 @@ namespace Barotrauma
msg.WriteRangedSingle(
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
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)
{
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(
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
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)
{
msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], periodicEffect.MinInterval, periodicEffect.MaxInterval, 8);

View File

@@ -86,6 +86,28 @@ namespace Barotrauma
{
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()

View File

@@ -1,8 +1,4 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
namespace Barotrauma.Abilities
{
@@ -31,7 +27,7 @@ namespace Barotrauma.Abilities
}
}
if (!SelectedItemHasTag(closestCharacter)) { return; }
if (closestCharacter == null || !SelectedItemHasTag(closestCharacter)) { return; }
if (closestDistance < squaredMaxDistance)
{

View File

@@ -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));
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;
NewMessage(msg, color.Value, isCommand: false, isError: true);
@@ -2407,7 +2411,7 @@ namespace Barotrauma
}
#endif
ShowError(error);
LogError(error);
}
public static void AddWarning(string warning)

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{

View File

@@ -38,10 +38,12 @@ namespace Barotrauma
}
IEnumerable<Affliction> afflictions = target.CharacterHealth.GetAllAfflictions().Where(affliction =>
{
LimbType? limbType = target.CharacterHealth.GetAfflictionLimb(affliction)?.type;
if (limbType == null) { return false; }
return limbType == TargetLimb && affliction.Strength >= MinStrength;
if (affliction.Prefab.LimbSpecific)
{
LimbType? limbType = target.CharacterHealth.GetAfflictionLimb(affliction)?.type;
if (limbType == null || limbType != TargetLimb) { return false; }
}
return affliction.Strength >= MinStrength;
});
if (afflictions.Any(a => a.Identifier == Identifier)) { return true; }

View File

@@ -13,7 +13,7 @@ namespace Barotrauma
{
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())
{
@@ -25,7 +25,7 @@ namespace Barotrauma
}
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";
@@ -52,7 +52,7 @@ namespace Barotrauma
}
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)
{

View File

@@ -18,10 +18,14 @@ class CheckConnectionAction : BinaryOptionAction
[Serialize("", IsPropertySaveable.Yes)]
public Identifier OtherConnectionName { get; set; }
[Serialize(1, IsPropertySaveable.Yes)]
public int MinAmount { get; set; }
public CheckConnectionAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
protected override bool? DetermineSuccess()
{
int amount = 0;
var connectTargets = !ConnectedItemTag.IsEmpty ? ParentEvent.GetTargets(ConnectedItemTag) : Enumerable.Empty<Entity>();
foreach (var target in ParentEvent.GetTargets(ItemTag))
{
@@ -33,27 +37,17 @@ class CheckConnectionAction : BinaryOptionAction
if (!IsCorrectConnection(connection, ConnectionName)) { continue; }
if (ConnectedItemTag.IsEmpty && OtherConnectionName.IsEmpty)
{
if (connection.Wires.Any()) { return true; }
amount += connection.Wires.Count();
if (amount >= MinAmount) { return true; }
continue;
}
foreach (var wire in connection.Wires)
{
if (wire.OtherConnection(connection) is not Connection otherConnection) { continue; }
if (ConnectedItemTag.IsEmpty)
{
if (IsCorrectConnection(otherConnection, OtherConnectionName)) { return true; }
}
else if (OtherConnectionName.IsEmpty)
{
if (IsCorrectItem()) { return true; }
}
else
{
if (!IsCorrectConnection(otherConnection, OtherConnectionName)) { continue; }
if (!IsCorrectItem()) { continue; }
return true;
}
if (!ConnectedItemTag.IsEmpty && !IsCorrectConnection(otherConnection, OtherConnectionName)) { continue; }
if (!ConnectedItemTag.IsEmpty && !IsCorrectItem()) { continue; }
amount++;
if (amount >= MinAmount) { return true; }
bool IsCorrectItem() => connectTargets.Contains(otherConnection.Item);
}

View File

@@ -1,7 +1,6 @@
#nullable enable
using System;
using System.Linq;
using System.Xml.Linq;
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()
{
if (!(GameMain.GameSession?.GameMode is CampaignMode campaignMode)) { return false; }
if (GameMain.GameSession?.GameMode is not CampaignMode campaignMode) { return false; }
string[] splitString = Condition.Split(' ');
string value = Condition;
string value;
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));
}
else

View File

@@ -1,4 +1,5 @@
using System;
using Barotrauma.Items.Components;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
@@ -15,8 +16,19 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes)]
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)]
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[] itemTags;
@@ -25,6 +37,19 @@ namespace Barotrauma
{
itemIdentifierSplit = ItemIdentifiers.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()
@@ -35,25 +60,70 @@ namespace Barotrauma
{
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 (itemIdentifierSplit.Any(identifier => character.HasEquippedItem(identifier))) { return true; }
return false;
if (!ApplyTagToTarget.IsEmpty)
{
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; }
if (itemIdentifierSplit.Any(identifier => inventory.FindItemByIdentifier(identifier, recursive: true) is not null)) { return true; }
int i = 0;
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;
}
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()
{
return $"{ToolBox.GetDebugSymbol(HasBeenDetermined())} {nameof(CheckItemAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +

View File

@@ -11,6 +11,9 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes)]
public Identifier OrderOption { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier OrderTargetTag { get; set; }
public CheckOrderAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
protected override bool? DetermineSuccess()
@@ -29,20 +32,17 @@ namespace Barotrauma
}
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;
}
var currentOrderInfo = targetCharacter.GetCurrentOrderWithTopPriority();
if (currentOrderInfo?.Identifier == OrderIdentifier)
{
if (OrderOption.IsEmpty)
if (!OrderTargetTag.IsEmpty)
{
return true;
}
else
{
return currentOrderInfo?.Option == OrderOption;
if (currentOrderInfo.TargetEntity is not Item targetItem || !targetItem.HasTag(OrderTargetTag)) { return false; }
}
return OrderOption.IsEmpty || currentOrderInfo?.Option == OrderOption;
}
return false;
}

View File

@@ -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";
}
}
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Barotrauma
{
class CheckSelectedItemAction : BinaryOptionAction
class CheckSelectedAction : BinaryOptionAction
{
public enum SelectedItemType { Primary, Secondary, Any };
@@ -16,7 +16,7 @@ namespace Barotrauma
[Serialize(SelectedItemType.Any, IsPropertySaveable.Yes)]
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()
{
@@ -34,7 +34,7 @@ namespace Barotrauma
}
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;
}
if (!TargetTag.IsEmpty)
@@ -42,11 +42,16 @@ namespace Barotrauma
IEnumerable<Entity> targets = ParentEvent.GetTargets(TargetTag);
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;
}
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)
{
continue;

View File

@@ -134,7 +134,7 @@ namespace Barotrauma
public static EventAction Instantiate(ScriptedEvent scriptedEvent, ContentXElement element)
{
Type actionType = null;
Type actionType;
try
{
actionType = Type.GetType("Barotrauma." + element.Name, true, true);

View File

@@ -1,8 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class GodModeAction : EventAction
@@ -10,6 +5,9 @@ namespace Barotrauma
[Serialize(true, IsPropertySaveable.Yes)]
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)]
public Identifier TargetTag { get; set; }
@@ -35,9 +33,16 @@ namespace Barotrauma
{
if (target != null && target is Character character)
{
character.GodMode = Enabled;
if (UpdateAfflictions)
{
character.CharacterHealth.Unkillable = Enabled;
}
else
{
character.GodMode = Enabled;
}
}
}
}
isFinished = true;
}

View File

@@ -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;
}

View File

@@ -2,7 +2,7 @@ namespace Barotrauma
{
partial class MessageBoxAction : EventAction
{
public enum ActionType { Create, Close }
public enum ActionType { Create, ConnectObjective, Close, Clear }
[Serialize(ActionType.Create, IsPropertySaveable.Yes)]
public ActionType Type { get; set; }
@@ -51,7 +51,13 @@ namespace Barotrauma
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)
{

View File

@@ -55,7 +55,7 @@ namespace Barotrauma
if (GameMain.GameSession.GameMode is CampaignMode campaign)
{
MissionPrefab prefab = null;
Mission unlockedMission = null;
var unlockLocation = FindUnlockLocation();
if (unlockLocation == null && CreateLocationIfNotFound)
{
@@ -72,27 +72,34 @@ namespace Barotrauma
{
if (!MissionIdentifier.IsEmpty)
{
prefab = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier);
unlockedMission = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier);
}
else if (!MissionTag.IsEmpty)
{
prefab = unlockLocation.UnlockMissionByTag(MissionTag);
unlockedMission = unlockLocation.UnlockMissionByTag(MissionTag);
}
if (campaign is MultiPlayerCampaign mpCampaign)
{
mpCampaign.IncrementLastUpdateIdForFlag(MultiPlayerCampaign.NetFlags.MapAndMissions);
}
if (prefab != null)
if (unlockedMission != null)
{
DebugConsole.NewMessage($"Unlocked mission \"{prefab.Name}\" in the location \"{unlockLocation.Name}\".");
#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))
if (unlockedMission.Locations[0] == unlockedMission.Locations[1] || unlockedMission.Locations[1] ==null)
{
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
NotifyMissionUnlock(prefab);
#else
NotifyMissionUnlock(unlockedMission);
#endif
}
}
@@ -138,16 +145,17 @@ namespace Barotrauma
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(MissionAction)} -> ({(MissionIdentifier.IsEmpty ? MissionTag : MissionIdentifier)})";
}
#if SERVER
private void NotifyMissionUnlock(MissionPrefab prefab)
private void NotifyMissionUnlock(Mission mission)
{
foreach (Client client in GameMain.Server.ConnectedClients)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
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);
}
}

View File

@@ -16,6 +16,9 @@ namespace Barotrauma
[Serialize(false, IsPropertySaveable.Yes)]
public bool AddToCrew { get; set; }
[Serialize(false, IsPropertySaveable.Yes)]
public bool RemoveFromCrew { get; set; }
private bool isFinished = false;
public NPCChangeTeamAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
@@ -35,34 +38,47 @@ namespace Barotrauma
if (AddToCrew && (TeamTag == CharacterTeamType.Team1 || TeamTag == CharacterTeamType.Team2))
{
npc.Info.StartItemsGiven = true;
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)
{
item.AllowStealing = true;
var wifiComponent = item.GetComponent<Items.Components.WifiComponent>();
if (wifiComponent != null)
item.AllowStealing = allowStealing;
if (item.GetComponent<Items.Components.WifiComponent>() is { } wifiComponent)
{
wifiComponent.TeamID = TeamTag;
}
var idCard = item.GetComponent<Items.Components.IdCard>();
if (idCard != null)
if (item.GetComponent<Items.Components.IdCard>() is { } idCard)
{
idCard.TeamID = TeamTag;
idCard.SubmarineSpecificID = 0;
}
}
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 == Submarine.MainSub && wp.SpawnType == SpawnType.Human);
WayPoint subWaypoint =
WayPoint.WayPointList.Find(wp => wp.Submarine == sub && wp.SpawnType == SpawnType.Human && wp.AssignedJob == npc.Info.Job?.Prefab) ??
WayPoint.WayPointList.Find(wp => wp.Submarine == sub && wp.SpawnType == SpawnType.Human);
if (subWaypoint != null)
{
npc.GiveIdCardTags(subWaypoint, createNetworkEvent: true);
}
#if SERVER
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.AddToCrewEventData(TeamTag, npc.Inventory.AllItems));
#endif
}
}
isFinished = true;

View File

@@ -1,8 +1,5 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -17,6 +14,12 @@ namespace Barotrauma
[Serialize(true, IsPropertySaveable.Yes)]
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;
public NPCFollowAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
@@ -32,6 +35,7 @@ namespace Barotrauma
target = ParentEvent.GetTargets(TargetTag).FirstOrDefault();
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)
{
@@ -56,6 +60,11 @@ namespace Barotrauma
}
}
}
targetCount++;
if (MaxTargets > -1 && targetCount >= MaxTargets)
{
break;
}
}
isFinished = true;
}
@@ -67,11 +76,11 @@ namespace Barotrauma
public override void Reset()
{
if (affectedNpcs != null && target != null)
if (affectedNpcs != null && target != null && AbandonOnReset)
{
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>())
{
if (goToObjective.Target == target)

View File

@@ -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()})";
}
}
}

View File

@@ -29,7 +29,7 @@ namespace Barotrauma
foreach (var npc in affectedNpcs)
{
if (!(npc.AIController is HumanAIController humanAiController)) { continue; }
if (npc.AIController is not HumanAIController humanAiController) { continue; }
if (Wait)
{
@@ -62,7 +62,7 @@ namespace Barotrauma
{
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)
{
gotoObjective.Abandon = true;

View File

@@ -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.")]
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.")]
public string TargetModuleTags
{
@@ -127,7 +130,7 @@ namespace Barotrauma
ISpatialEntity spawnPos = GetSpawnPos();
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; }
newCharacter.HumanPrefab = humanPrefab;
@@ -162,7 +165,7 @@ namespace Barotrauma
ISpatialEntity spawnPos = GetSpawnPos();
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)
{
@@ -208,7 +211,7 @@ namespace Barotrauma
ISpatialEntity spawnPos = GetSpawnPos();
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

View File

@@ -13,7 +13,7 @@ class TutorialIconAction : EventAction
public Identifier TargetTag { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public string IconStyle { get; set; }
public Identifier IconStyle { get; set; }
private bool isFinished;
@@ -33,7 +33,7 @@ class TutorialIconAction : EventAction
}
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)
{
@@ -41,7 +41,7 @@ class TutorialIconAction : EventAction
}
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)
{

View File

@@ -8,7 +8,7 @@ namespace Barotrauma
public SegmentActionType Type { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier Id { get; set; }
public Identifier Identifier { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier ObjectiveTag { get; set; }
@@ -30,7 +30,13 @@ namespace Barotrauma
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)
{

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