Unstable 1.8.4.0
This commit is contained in:
@@ -166,7 +166,7 @@ namespace Barotrauma
|
||||
// check if the store can afford the item
|
||||
if (store.Balance < itemValue) { continue; }
|
||||
// TODO: Write logic for prioritizing certain items over others (e.g. lone Battery Cell should be preferred over one inside a Stun Baton)
|
||||
var matchingItems = sellableItems.Where(i => i.Prefab == item.ItemPrefab);
|
||||
var matchingItems = sellableItems.Where(i => i.Prefab.Identifier == item.ItemPrefabIdentifier);
|
||||
int count = Math.Min(item.Quantity, matchingItems.Count());
|
||||
SoldItem.SellOrigin origin = sellingMode == Store.StoreTab.Sell ? SoldItem.SellOrigin.Character : SoldItem.SellOrigin.Submarine;
|
||||
if (origin == SoldItem.SellOrigin.Character || GameMain.IsSingleplayer)
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Barotrauma
|
||||
public GUIComponent ReportButtonFrame { get; set; }
|
||||
|
||||
private GUIFrame guiFrame;
|
||||
private GUIFrame crewArea;
|
||||
private GUILayoutGroup crewArea;
|
||||
private GUIListBox crewList;
|
||||
private float crewListOpenState;
|
||||
private bool _isCrewMenuOpen = true;
|
||||
@@ -47,7 +47,7 @@ namespace Barotrauma
|
||||
|
||||
/// <summary>
|
||||
/// This property stores the preference in settings. Don't use for automatic logic.
|
||||
/// Use AutoShowCrewList(), AutoHideCrewList(), and ResetCrewList().
|
||||
/// Use AutoHideCrewList(), and ResetCrewList().
|
||||
/// </summary>
|
||||
public bool IsCrewMenuOpen
|
||||
{
|
||||
@@ -62,11 +62,9 @@ namespace Barotrauma
|
||||
|
||||
public static bool PreferCrewMenuOpen = true;
|
||||
|
||||
public bool AutoShowCrewList() => _isCrewMenuOpen = true;
|
||||
|
||||
public void AutoHideCrewList() => _isCrewMenuOpen = false;
|
||||
|
||||
public void ResetCrewList() => _isCrewMenuOpen = PreferCrewMenuOpen;
|
||||
public void ResetCrewListOpenState() => _isCrewMenuOpen = PreferCrewMenuOpen;
|
||||
|
||||
const float CommandNodeAnimDuration = 0.2f;
|
||||
|
||||
@@ -93,12 +91,30 @@ namespace Barotrauma
|
||||
|
||||
#region Crew Area
|
||||
|
||||
crewArea = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform), style: null, color: Color.Transparent)
|
||||
crewArea = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
CanBeFocused = false
|
||||
Stretch = true
|
||||
};
|
||||
crewArea.RectTransform.NonScaledSize = HUDLayoutSettings.CrewArea.Size;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
CharacterTeamType teamId = i == 0 ? CharacterTeamType.Team1 : CharacterTeamType.Team2;
|
||||
var nameText = new GUITextBlock(new RectTransform(new Point(crewArea.Rect.Width - GUI.IntScale(10), GUI.IntScale(30)), crewArea.RectTransform), CombatMission.GetTeamName(teamId), textColor: CombatMission.GetTeamColor(teamId))
|
||||
{
|
||||
ForceUpperCase = ForceUpperCase.Yes,
|
||||
TextGetter = () => CombatMission.GetTeamName(teamId),
|
||||
Visible = false,
|
||||
IgnoreLayoutGroups = true,
|
||||
UserData = teamId
|
||||
};
|
||||
var teamIcon = new GUIImage(new RectTransform(Vector2.One, nameText.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.BothHeight), style: i == 0 ? "CoalitionIcon" : "SeparatistIcon")
|
||||
{
|
||||
Color = nameText.TextColor
|
||||
};
|
||||
nameText.Padding = new Vector4(teamIcon.Rect.Width + nameText.Padding.X, nameText.Padding.Y, nameText.Padding.Z, nameText.Padding.W);
|
||||
}
|
||||
|
||||
// AbsoluteOffset is set in UpdateProjectSpecific based on crewListOpenState
|
||||
crewList = new GUIListBox(new RectTransform(Vector2.One, crewArea.RectTransform), style: null, isScrollBarOnDefaultSide: false)
|
||||
{
|
||||
@@ -177,6 +193,12 @@ namespace Barotrauma
|
||||
|
||||
ChatBox.InputBox.OnTextChanged += ChatBox.TypingChatMessage;
|
||||
}
|
||||
else if (GameMain.Client == null)
|
||||
{
|
||||
//this method would throw a non-descriptive nullref exception later when trying to access the chatbox
|
||||
//if we'd try to continue from here, better to throw a more descriptive one at this point
|
||||
throw new InvalidOperationException($"Attempted to initialize {nameof(CrewManager)} for multiplayer, but no multiplayer client is active. Are you trying to load a multiplayer save in singleplayer?");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -290,6 +312,14 @@ namespace Barotrauma
|
||||
|
||||
#region Character list management
|
||||
|
||||
/// <summary>
|
||||
/// Note: this is only works client-side. TODO: make it work server-side too?
|
||||
/// </summary>
|
||||
public IEnumerable<Character> GetCharacters()
|
||||
{
|
||||
return characters;
|
||||
}
|
||||
|
||||
public Rectangle GetActiveCrewArea()
|
||||
{
|
||||
return crewArea.Rect;
|
||||
@@ -562,9 +592,19 @@ namespace Barotrauma
|
||||
public bool CharacterClicked(GUIComponent component, object selection)
|
||||
{
|
||||
if (!AllowCharacterSwitch) { return false; }
|
||||
if (!(selection is Character character) || character.IsDead || character.IsUnconscious) { return false; }
|
||||
if (selection is not Character character || character.IsDead || character.IsUnconscious) { return false; }
|
||||
if (!character.IsOnPlayerTeam) { return false; }
|
||||
|
||||
if (GameMain.IsMultiplayer)
|
||||
{
|
||||
if (Character.Controlled == null)
|
||||
{
|
||||
Camera cam = Screen.Selected.Cam;
|
||||
cam.Position = character.DrawPosition;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SelectCharacter(character);
|
||||
if (GUI.KeyboardDispatcher.Subscriber == crewList) { GUI.KeyboardDispatcher.Subscriber = null; }
|
||||
return true;
|
||||
@@ -631,10 +671,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (crewList != this.crewList) { return; }
|
||||
if (draggedElementData is not Character) { return; }
|
||||
if (!IsSinglePlayer) { return; }
|
||||
if (crewList.HasDraggedElementIndexChanged)
|
||||
{
|
||||
UpdateCrewListIndices();
|
||||
if (IsSinglePlayer) { UpdateCrewListIndices(); }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -645,10 +684,13 @@ namespace Barotrauma
|
||||
private void ResetCrewListIndex(Character c)
|
||||
{
|
||||
if (c?.Info == null) { return; }
|
||||
c.Info.CrewListIndex = -1;
|
||||
UpdateCrewListIndices();
|
||||
//default to the bottom of the list
|
||||
c.Info.CrewListIndex = int.MaxValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the <see cref="CharacterInfo.CrewListIndex"/> of the characters based on their order in the crew list
|
||||
/// </summary>
|
||||
private void UpdateCrewListIndices()
|
||||
{
|
||||
if (crewList == null) { return; }
|
||||
@@ -661,20 +703,23 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order the crew list according to the characters' <see cref="CharacterInfo.CrewListIndex"/>
|
||||
/// </summary>
|
||||
private void SortCrewList()
|
||||
{
|
||||
if (crewList == null) { return; }
|
||||
crewList.Content.RectTransform.SortChildren((x, y) =>
|
||||
{
|
||||
var infoX = (x.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
|
||||
var infoY = (y.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
|
||||
if (infoX.HasValue)
|
||||
int? index1 = (x.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
|
||||
int? index2 = (y.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
|
||||
if (index1.HasValue)
|
||||
{
|
||||
return infoY.HasValue ? infoX.Value.CompareTo(infoY.Value) : -1;
|
||||
return index2.HasValue ? index1.Value.CompareTo(index2.Value) : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return infoY.HasValue ? 1 : 0;
|
||||
return index2.HasValue ? 1 : 0;
|
||||
}
|
||||
});
|
||||
UpdateCrewListIndices();
|
||||
@@ -1312,6 +1357,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (ConversationAction.IsDialogOpen) { return; }
|
||||
if (!AllowCharacterSwitch) { return; }
|
||||
if (character == null || character.Removed) { return; }
|
||||
//make the previously selected character wait in place for some time
|
||||
//(so they don't immediately start idling and walking away from their station)
|
||||
var aiController = Character.Controlled?.AIController;
|
||||
@@ -1323,6 +1369,16 @@ namespace Barotrauma
|
||||
{
|
||||
GameSession.TabMenuInstance.SelectInfoFrameTab(TabMenu.SelectedTab);
|
||||
}
|
||||
if (character.SelectedItem?.GetComponent<Controller>() == null && character.SelectedCharacter == null)
|
||||
{
|
||||
ResetCrewListOpenState();
|
||||
ChatBox.ResetChatBoxOpenState();
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoHideCrewList();
|
||||
ChatBox.AutoHideChatBox();
|
||||
}
|
||||
}
|
||||
|
||||
private int TryAdjustIndex(int amount)
|
||||
@@ -1340,7 +1396,7 @@ namespace Barotrauma
|
||||
if (index > lastIndex) { index = 0; }
|
||||
if (index < 0) { index = lastIndex; }
|
||||
|
||||
if ((crewList.Content.GetChild(index)?.UserData as Character)?.IsOnPlayerTeam ?? false)
|
||||
if (crewList.Content.GetChild(index)?.UserData is Character { IsOnPlayerTeam: true, Removed: false })
|
||||
{
|
||||
return index;
|
||||
}
|
||||
@@ -1635,6 +1691,18 @@ namespace Barotrauma
|
||||
{
|
||||
crewArea.Visible = characters.Count > 0 && CharacterHealth.OpenHealthWindow == null;
|
||||
|
||||
CharacterTeamType myTeam = Character.Controlled?.TeamID ?? GameMain.Client?.MyClient?.TeamID ?? CharacterTeamType.Team1;
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
var team1Text = crewArea.GetChildByUserData(CharacterTeamType.Team1);
|
||||
team1Text.Visible = myTeam == CharacterTeamType.Team1;
|
||||
team1Text.IgnoreLayoutGroups = !team1Text.Visible;
|
||||
|
||||
var team2Text = crewArea.GetChildByUserData(CharacterTeamType.Team2);
|
||||
team2Text.Visible = myTeam == CharacterTeamType.Team2;
|
||||
team2Text.IgnoreLayoutGroups = !team2Text.Visible;
|
||||
}
|
||||
|
||||
foreach (GUIComponent characterComponent in crewList.Content.Children)
|
||||
{
|
||||
if (characterComponent.UserData is Character character)
|
||||
@@ -1645,7 +1713,7 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
|
||||
characterComponent.Visible = Character.Controlled == null || Character.Controlled.TeamID == character.TeamID;
|
||||
characterComponent.Visible = myTeam == character.TeamID;
|
||||
if (character.TeamID == CharacterTeamType.FriendlyNPC && Character.Controlled != null &&
|
||||
(character.CurrentHull == Character.Controlled.CurrentHull || Vector2.DistanceSquared(Character.Controlled.WorldPosition, character.WorldPosition) < 500.0f * 500.0f))
|
||||
{
|
||||
@@ -1873,7 +1941,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GameMain.GameSession?.CrewManager == null)
|
||||
if (GameMain.GameSession?.CrewManager == null || Screen.Selected is { IsEditor: true })
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -2010,7 +2078,7 @@ namespace Barotrauma
|
||||
// Character context works differently to others as we still use the "basic" command interface,
|
||||
// but the order will be automatically assigned to this character
|
||||
isContextual = forceContextual;
|
||||
if (entityContext is Character character)
|
||||
if (entityContext is Character { Info: not null } character)
|
||||
{
|
||||
characterContext = character;
|
||||
itemContext = null;
|
||||
@@ -2098,7 +2166,7 @@ namespace Barotrauma
|
||||
CreateNodeConnectors();
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
Character.Controlled.dontFollowCursor = true;
|
||||
Character.Controlled.FollowCursor = false;
|
||||
}
|
||||
|
||||
HintManager.OnShowCommandInterface();
|
||||
@@ -2242,7 +2310,7 @@ namespace Barotrauma
|
||||
returnNodeHotkey = expandNodeHotkey = Keys.None;
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
Character.Controlled.dontFollowCursor = false;
|
||||
Character.Controlled.FollowCursor = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2511,7 +2579,7 @@ namespace Barotrauma
|
||||
// --> Create shortcut node for Steer order
|
||||
if (CanFitMoreNodes() && ShouldDelegateOrder("steer") && IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["steer"]) &&
|
||||
subItems.Find(i => i.HasTag(Tags.NavTerminal) && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedItem == nav) &&
|
||||
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
|
||||
nav.GetComponent<Steering>() is Steering { HasPower: true } steering)
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs["steer"], steering.Item, steering);
|
||||
AddOrderNode(order);
|
||||
@@ -2625,9 +2693,9 @@ namespace Barotrauma
|
||||
bool IsNonDuplicateOrder(Order order) => IsNonDuplicateOrderPrefab(order.Prefab, order.Option);
|
||||
bool IsNonDuplicateOrderPrefab(OrderPrefab orderPrefab, Identifier option = default)
|
||||
{
|
||||
return characterContext == null || (option.IsEmpty ?
|
||||
return characterContext == null || (characterContext.CurrentOrders != null && (option.IsEmpty ?
|
||||
characterContext.CurrentOrders.None(oi => oi?.Identifier == orderPrefab?.Identifier) :
|
||||
characterContext.CurrentOrders.None(oi => oi?.Identifier == orderPrefab?.Identifier && oi.Option == option));
|
||||
characterContext.CurrentOrders.None(oi => oi?.Identifier == orderPrefab?.Identifier && oi?.Option == option)));
|
||||
}
|
||||
void AddOrderNodeWithIdentifier(string identifier)
|
||||
{
|
||||
@@ -3000,7 +3068,7 @@ namespace Barotrauma
|
||||
{
|
||||
optionElement.OnSecondaryClicked = (button, _) => CreateAssignmentNodes(button);
|
||||
}
|
||||
var colorMultiplier = characters.Any(c => c.CurrentOrders.Any(o => o != null &&
|
||||
var colorMultiplier = characters.Any(c => c.CurrentOrders != null && c.CurrentOrders.Any(o => o != null &&
|
||||
o.Identifier == userData.Order.Identifier &&
|
||||
o.TargetEntity == userData.Order.TargetEntity)) ? 0.5f : 1f;
|
||||
CreateNodeIcon(Vector2.One, optionElement.RectTransform, item.Prefab.MinimapIcon ?? order.SymbolSprite, order.Color * colorMultiplier, tooltip: item.Name);
|
||||
@@ -3654,7 +3722,7 @@ namespace Barotrauma
|
||||
bool hasLeaks = Character.Controlled.CurrentHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f);
|
||||
ToggleReportButton("reportbreach", hasLeaks);
|
||||
|
||||
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled, false));
|
||||
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled, targetCharactersInOtherSubs: false));
|
||||
ToggleReportButton("reportintruders", hasIntruders);
|
||||
|
||||
foreach (GUIComponent reportButton in ReportButtonFrame.Children)
|
||||
@@ -3780,5 +3848,72 @@ namespace Barotrauma
|
||||
GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime);
|
||||
}
|
||||
}
|
||||
|
||||
private class CharacterInfoComparer : IEqualityComparer<CharacterInfo>
|
||||
{
|
||||
public bool Equals(CharacterInfo x, CharacterInfo y)
|
||||
{
|
||||
if (ReferenceEquals(x, y))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (x is null || y is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return x.ID == y.ID;
|
||||
}
|
||||
|
||||
public int GetHashCode(CharacterInfo obj)
|
||||
{
|
||||
return obj.ID;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UpdateReserveBenchIfNeeded(IEnumerable<CharacterInfo> updatedReserveBench)
|
||||
{
|
||||
var newBench = updatedReserveBench.ToHashSet(new CharacterInfoComparer());
|
||||
var currentBench = reserveBench.ToHashSet(new CharacterInfoComparer());
|
||||
|
||||
bool updateNeeded = !newBench.SetEquals(currentBench);
|
||||
if (updateNeeded)
|
||||
{
|
||||
reserveBench.Clear(); // since this is the reserve bench (characters not instantiated), there's no need to retain any references etc
|
||||
reserveBench.AddRange(updatedReserveBench);
|
||||
}
|
||||
|
||||
return updateNeeded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will update which CharacterInfos should be in CrewManager and which shouldn't, excluding the reserve bench.
|
||||
/// The CharacterInfos themselves aren't updated, they will only be either added, removed, or kept as-is.
|
||||
/// </summary>
|
||||
public bool UpdateCrewManagerIfNecessary(List<CharacterInfo> updatedCrewManager)
|
||||
{
|
||||
// CharacterInfos no longer in the server's CrewManager
|
||||
var toRemove = characterInfos.Where(original => updatedCrewManager.None(updated => updated.ID == original.ID)).ToList();
|
||||
// CharacterInfos that are in the server's CrewManager but not on the client yet
|
||||
var toAdd = updatedCrewManager.Where(updated => characterInfos.None(original => original.ID == updated.ID)).ToList();
|
||||
|
||||
foreach (CharacterInfo characterInfo in toRemove)
|
||||
{
|
||||
if (characterInfo.Character is Character existingCharacter)
|
||||
{
|
||||
if (!existingCharacter.IsBot) { continue; } // on client side players are also stored here, we should skip those in this case
|
||||
RemoveCharacter(characterInfo.Character, removeInfo: true, resetCrewListIndex: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
characterInfos.Remove(characterInfo);
|
||||
}
|
||||
}
|
||||
|
||||
characterInfos.AddRange(toAdd);
|
||||
|
||||
return toRemove.Count > 0 || toAdd.Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ namespace Barotrauma
|
||||
default:
|
||||
ShowCampaignUI = true;
|
||||
CampaignUI.SelectTab(npc.CampaignInteractionType, npc);
|
||||
CampaignUI.UpgradeStore?.RequestRefresh();
|
||||
CampaignUI.UpgradeStore?.RequestRefresh(refreshUpgrades: true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -395,13 +395,15 @@ namespace Barotrauma
|
||||
|
||||
protected void TryEndRoundWithFuelCheck(Action onConfirm, Action onReturnToMapScreen)
|
||||
{
|
||||
if (Submarine.MainSub == null) { return; }
|
||||
|
||||
Submarine.MainSub.CheckFuel();
|
||||
bool lowFuel = Submarine.MainSub.Info.LowFuel;
|
||||
if (PendingSubmarineSwitch != null)
|
||||
{
|
||||
lowFuel = TransferItemsOnSubSwitch ? (lowFuel && PendingSubmarineSwitch.LowFuel) : PendingSubmarineSwitch.LowFuel;
|
||||
}
|
||||
if (Level.IsLoadedFriendlyOutpost && lowFuel && CargoManager.PurchasedItems.None(i => i.Value.Any(pi => pi.ItemPrefab.Tags.Contains("reactorfuel"))))
|
||||
if (Level.IsLoadedFriendlyOutpost && lowFuel && CargoManager.PurchasedItems.None(i => i.Value.Any(pi => pi.ItemPrefab.Tags.Contains(Tags.ReactorFuel))))
|
||||
{
|
||||
var extraConfirmationBox =
|
||||
new GUIMessageBox(TextManager.Get("lowfuelheader"),
|
||||
|
||||
+63
-24
@@ -532,6 +532,7 @@ namespace Barotrauma
|
||||
|
||||
bool isFirstRound = msg.ReadBoolean();
|
||||
byte campaignID = msg.ReadByte();
|
||||
byte roundId = msg.ReadByte();
|
||||
UInt16 saveID = msg.ReadUInt16();
|
||||
string mapSeed = msg.ReadString();
|
||||
|
||||
@@ -541,7 +542,7 @@ namespace Barotrauma
|
||||
{
|
||||
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
|
||||
|
||||
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Empty, mapSeed);
|
||||
GameMain.GameSession = new GameSession(null, Option.None, CampaignDataPath.CreateRegular(savePath), GameModePreset.MultiPlayerCampaign, CampaignSettings.Empty, mapSeed);
|
||||
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
|
||||
campaign.CampaignID = campaignID;
|
||||
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
|
||||
@@ -553,7 +554,7 @@ namespace Barotrauma
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.Misc))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (Misc)");
|
||||
DebugConsole.Log("Received campaign update (Misc), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
bool purchasedHullRepairs = msg.ReadBoolean();
|
||||
bool purchasedItemRepairs = msg.ReadBoolean();
|
||||
@@ -571,7 +572,7 @@ namespace Barotrauma
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.MapAndMissions))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (MapAndMissions)");
|
||||
DebugConsole.Log("Received campaign update (MapAndMissions), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
bool forceMapUI = msg.ReadBoolean();
|
||||
bool allowDebugTeleport = msg.ReadBoolean();
|
||||
@@ -634,7 +635,7 @@ namespace Barotrauma
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.SubList))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (SubList)");
|
||||
DebugConsole.Log("Received campaign update (SubList), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
ushort ownedSubCount = msg.ReadUInt16();
|
||||
List<ushort> ownedSubIndices = new List<ushort>();
|
||||
@@ -679,7 +680,7 @@ namespace Barotrauma
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.UpgradeManager))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (UpgradeManager)");
|
||||
DebugConsole.Log("Received campaign update (UpgradeManager), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
|
||||
ushort pendingUpgradeCount = msg.ReadUInt16();
|
||||
@@ -737,7 +738,7 @@ namespace Barotrauma
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.ItemsInBuyCrate))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (ItemsInBuyCrate)");
|
||||
DebugConsole.Log("Received campaign update (ItemsInBuyCrate), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
var buyCrateItems = ReadPurchasedItems(msg, sender: null);
|
||||
if (ShouldApply(NetFlags.ItemsInBuyCrate, id, requireUpToDateSave: true))
|
||||
@@ -753,7 +754,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.ItemsInSellFromSubCrate))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (ItemsInSellFromSubCrate)");
|
||||
DebugConsole.Log("Received campaign update (ItemsInSellFromSubCrate), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
var subSellCrateItems = ReadPurchasedItems(msg, sender: null);
|
||||
if (ShouldApply(NetFlags.ItemsInSellFromSubCrate, id, requireUpToDateSave: true))
|
||||
@@ -769,7 +770,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.PurchasedItems))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (PuchasedItems)");
|
||||
DebugConsole.Log("Received campaign update (PuchasedItems), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
var purchasedItems = ReadPurchasedItems(msg, sender: null);
|
||||
if (ShouldApply(NetFlags.PurchasedItems, id, requireUpToDateSave: true))
|
||||
@@ -785,7 +786,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.SoldItems))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (SoldItems)");
|
||||
DebugConsole.Log("Received campaign update (SoldItems), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
var soldItems = ReadSoldItems(msg);
|
||||
if (ShouldApply(NetFlags.SoldItems, id, requireUpToDateSave: true))
|
||||
@@ -801,7 +802,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.Reputation))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (Reputation)");
|
||||
DebugConsole.Log("Received campaign update (Reputation), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>();
|
||||
byte factionsCount = msg.ReadByte();
|
||||
@@ -828,7 +829,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.CharacterInfo))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (CharacterInfo)");
|
||||
DebugConsole.Log("Received campaign update (CharacterInfo), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
bool hasCharacterData = msg.ReadBoolean();
|
||||
CharacterInfo myCharacterInfo = null;
|
||||
@@ -837,16 +838,27 @@ namespace Barotrauma
|
||||
{
|
||||
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg, requireJobPrefabFound: !waitForModsDownloaded);
|
||||
}
|
||||
if (!waitForModsDownloaded && ShouldApply(NetFlags.CharacterInfo, id, requireUpToDateSave: true))
|
||||
//don't require the correct round ID for the character info if we're in the lobby
|
||||
// = allow updating the character to the latest one in the lobby, even though we've not loaded to the same round as the server
|
||||
if (!waitForModsDownloaded && ShouldApply(NetFlags.CharacterInfo, id, requireUpToDateSave: true, requireCorrectRoundId: Screen.Selected != GameMain.NetLobbyScreen))
|
||||
{
|
||||
if (myCharacterInfo != null)
|
||||
{
|
||||
GameMain.Client.CharacterInfo = myCharacterInfo;
|
||||
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo);
|
||||
GameMain.GameSession.RefreshAnyOpenPlayerInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
//don't reset the character info nor the open UI here here,
|
||||
//the client needs it to be able to customize the character they want to next spawn as
|
||||
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
|
||||
//if we've already discarded our current character and the server is just "verifying" that,
|
||||
//no need to refresh the UI (no changes, refreshing would just throw the client out of the character settings panel)
|
||||
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
|
||||
{
|
||||
GameMain.GameSession.RefreshAnyOpenPlayerInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -863,8 +875,14 @@ namespace Barotrauma
|
||||
}
|
||||
campaign.SuppressStateSending = false;
|
||||
|
||||
bool ShouldApply(NetFlags flag, UInt16 id, bool requireUpToDateSave)
|
||||
bool ShouldApply(NetFlags flag, UInt16 id, bool requireUpToDateSave, bool requireCorrectRoundId = true)
|
||||
{
|
||||
if (requireCorrectRoundId && roundId != campaign.RoundID)
|
||||
{
|
||||
DebugConsole.Log($"Received campaing update for a different round (client: {campaign.RoundID}, server: {roundId}), ignoring...");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NetIdUtils.IdMoreRecent(id, campaign.GetLastUpdateIdForFlag(flag)) &&
|
||||
(!requireUpToDateSave || saveID == campaign.LastSaveID))
|
||||
{
|
||||
@@ -919,26 +937,42 @@ namespace Barotrauma
|
||||
|
||||
ushort pendingHireLength = msg.ReadUInt16();
|
||||
List<UInt16> pendingHires = new List<UInt16>();
|
||||
bool[] pendingHiresToReserveBench = new bool[pendingHireLength];
|
||||
for (int i = 0; i < pendingHireLength; i++)
|
||||
{
|
||||
pendingHires.Add(msg.ReadUInt16());
|
||||
pendingHiresToReserveBench[i] = msg.ReadBoolean();
|
||||
}
|
||||
|
||||
ushort hiredLength = msg.ReadUInt16();
|
||||
List<CharacterInfo> hiredCharacters = new List<CharacterInfo>();
|
||||
for (int i = 0; i < hiredLength; i++)
|
||||
List<CharacterInfo> updatedCrewManager = new List<CharacterInfo>();
|
||||
ushort crewLength = msg.ReadUInt16();
|
||||
for (int i = 0; i < crewLength; i++)
|
||||
{
|
||||
CharacterInfo hired = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
|
||||
hired.Salary = msg.ReadInt32();
|
||||
hiredCharacters.Add(hired);
|
||||
CharacterInfo crewMember = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
|
||||
if (crewMember.IsNewHire)
|
||||
{
|
||||
hiredCharacters.Add(crewMember);
|
||||
}
|
||||
updatedCrewManager.Add(crewMember);
|
||||
}
|
||||
|
||||
bool crewManagerUpdated = GameMain.GameSession.CrewManager?.UpdateCrewManagerIfNecessary(updatedCrewManager) ?? false;
|
||||
|
||||
ushort reserveBenchLength = msg.ReadUInt16();
|
||||
List<CharacterInfo> updatedReserveBench = new List<CharacterInfo>();
|
||||
for (int i = 0; i < reserveBenchLength; i++)
|
||||
{
|
||||
CharacterInfo info = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
|
||||
updatedReserveBench.Add(info);
|
||||
}
|
||||
bool reserveBenchUpdated = GameMain.GameSession.CrewManager?.UpdateReserveBenchIfNeeded(updatedReserveBench) ?? false;
|
||||
|
||||
bool renameCrewMember = msg.ReadBoolean();
|
||||
if (renameCrewMember)
|
||||
{
|
||||
UInt16 renamedIdentifier = msg.ReadUInt16();
|
||||
string newName = msg.ReadString();
|
||||
CharacterInfo renamedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.ID == renamedIdentifier);
|
||||
CharacterInfo renamedCharacter = CrewManager.GetCharacterInfos(includeReserveBench: true).FirstOrDefault(info => info.ID == renamedIdentifier);
|
||||
if (renamedCharacter != null)
|
||||
{
|
||||
CrewManager.RenameCharacter(renamedCharacter, newName);
|
||||
@@ -955,7 +989,7 @@ namespace Barotrauma
|
||||
if (fireCharacter)
|
||||
{
|
||||
UInt16 firedIdentifier = msg.ReadUInt16();
|
||||
CharacterInfo firedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.ID == firedIdentifier);
|
||||
CharacterInfo firedCharacter = CrewManager.GetCharacterInfos(includeReserveBench: true).FirstOrDefault(info => info.ID == firedIdentifier);
|
||||
// this one might and is allowed to be null since the character is already fired on the original sender's game
|
||||
if (firedCharacter != null) { CrewManager.FireCharacter(firedCharacter); }
|
||||
}
|
||||
@@ -967,8 +1001,8 @@ namespace Barotrauma
|
||||
{
|
||||
CampaignUI.HRManagerUI.SetHireables(map.CurrentLocation, availableHires);
|
||||
if (hiredCharacters.Any()) { CampaignUI.HRManagerUI.ValidateHires(hiredCharacters, takeMoney: false, createNotification: createNotification); }
|
||||
CampaignUI.HRManagerUI.SetPendingHires(pendingHires, map.CurrentLocation);
|
||||
if (renameCrewMember || fireCharacter) { CampaignUI.HRManagerUI.UpdateCrew(); }
|
||||
//don't check the crew size limit: if the server says someone's hired, then it's so
|
||||
CampaignUI.HRManagerUI.SetPendingHires(pendingHires, pendingHiresToReserveBench, map.CurrentLocation, checkCrewSizeLimit: false);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -979,6 +1013,11 @@ namespace Barotrauma
|
||||
CurrentLocation?.ForceHireableCharacters(availableHires);
|
||||
}
|
||||
|
||||
if (fireCharacter || renameCrewMember || crewManagerUpdated || reserveBenchUpdated)
|
||||
{
|
||||
CampaignUI?.HRManagerUI?.RefreshHRView();
|
||||
GameMain.GameSession?.DeathPrompt?.UpdateBotList();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientReadMoney(IReadMessage inc)
|
||||
@@ -1044,7 +1083,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
public override void Save(XElement element, bool isSavingOnLoading)
|
||||
{
|
||||
//do nothing, the clients get the save files from the server
|
||||
}
|
||||
|
||||
+5
-5
@@ -240,7 +240,7 @@ namespace Barotrauma
|
||||
if (!savedOnStart)
|
||||
{
|
||||
GUI.SetSavingIndicatorState(true);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.DataPath, isSavingOnLoading: true);
|
||||
savedOnStart = true;
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ namespace Barotrauma
|
||||
if (success)
|
||||
{
|
||||
// Event history must be registered before ending the round or it will be cleared
|
||||
GameMain.GameSession.EventManager.RegisterEventHistory();
|
||||
GameMain.GameSession.EventManager.StoreEventDataAtRoundEnd();
|
||||
}
|
||||
GameMain.GameSession.EndRound("", transitionType);
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
@@ -448,7 +448,7 @@ namespace Barotrauma
|
||||
if (success)
|
||||
{
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.DataPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -479,7 +479,7 @@ namespace Barotrauma
|
||||
protected override void EndCampaignProjSpecific()
|
||||
{
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.DataPath);
|
||||
GameMain.CampaignEndScreen.Select();
|
||||
GUI.DisableHUD = false;
|
||||
GameMain.CampaignEndScreen.OnFinished = () =>
|
||||
@@ -672,7 +672,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
public override void Save(XElement element, bool isSavingOnLoading)
|
||||
{
|
||||
XElement modeElement = new XElement("SinglePlayerCampaign",
|
||||
new XAttribute("purchasedlostshuttles", PurchasedLostShuttles),
|
||||
|
||||
@@ -7,7 +7,7 @@ using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class TestGameMode : GameMode
|
||||
partial class TestGameMode : GameMode
|
||||
{
|
||||
public Action OnRoundEnd;
|
||||
|
||||
@@ -22,18 +22,6 @@ namespace Barotrauma
|
||||
|
||||
private GUIButton createEventButton;
|
||||
|
||||
public TestGameMode(GameModePreset preset) : base(preset)
|
||||
{
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs.OrderBy(p => p.Identifier))
|
||||
{
|
||||
for (int i = 0; i < jobPrefab.InitialCount; i++)
|
||||
{
|
||||
var variant = Rand.Range(0, jobPrefab.Variants);
|
||||
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
base.Start();
|
||||
@@ -42,17 +30,24 @@ namespace Barotrauma
|
||||
foreach (Submarine submarine in Submarine.Loaded)
|
||||
{
|
||||
submarine.NeutralizeBallast();
|
||||
//normally the body would be made static during level generation,
|
||||
//but in the test mode we load the outpost/wreck/beacon as if it was a normal sub and need to do this manually
|
||||
if (submarine.Info.Type == SubmarineType.Outpost ||
|
||||
submarine.Info.Type == SubmarineType.OutpostModule ||
|
||||
submarine.Info.Type == SubmarineType.Wreck ||
|
||||
submarine.Info.Type == SubmarineType.BeaconStation)
|
||||
switch (submarine.Info.Type)
|
||||
{
|
||||
submarine.PhysicsBody.BodyType = FarseerPhysics.BodyType.Static;
|
||||
case SubmarineType.Outpost:
|
||||
case SubmarineType.OutpostModule:
|
||||
case SubmarineType.Wreck:
|
||||
case SubmarineType.BeaconStation:
|
||||
//normally the body would be made static during level generation,
|
||||
//but in the test mode we load the outpost/wreck/beacon as if it was a normal sub and need to do this manually
|
||||
submarine.PhysicsBody.BodyType = FarseerPhysics.BodyType.Static;
|
||||
if (submarine.Info.ShouldBeRuin)
|
||||
{
|
||||
submarine.Info.Type = SubmarineType.Ruin;
|
||||
}
|
||||
submarine.TeamID = submarine.Info.IsOutpost ? CharacterTeamType.FriendlyNPC : CharacterTeamType.None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (SpawnOutpost)
|
||||
{
|
||||
GenerateOutpost(Submarine.MainSub);
|
||||
|
||||
+2
-2
@@ -86,7 +86,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
GameMain.GameSession = new GameSession(subInfo, GameModePreset.Tutorial, missionPrefabs: null);
|
||||
GameMain.GameSession = new GameSession(subInfo, Option.None, GameModePreset.Tutorial, missionPrefabs: null);
|
||||
(GameMain.GameSession.GameMode as TutorialMode).Tutorial = this;
|
||||
|
||||
if (generationParams is not null)
|
||||
@@ -138,7 +138,7 @@ namespace Barotrauma.Tutorials
|
||||
character = Character.Create(charInfo, wayPoint.WorldPosition, "", isRemotePlayer: false, hasAi: false);
|
||||
character.TeamID = CharacterTeamType.Team1;
|
||||
Character.Controlled = character;
|
||||
character.GiveJobItems(null);
|
||||
character.GiveJobItems(isPvPMode: false, null);
|
||||
|
||||
var idCard = character.Inventory.FindItemByTag("identitycard".ToIdentifier());
|
||||
if (idCard == null)
|
||||
|
||||
@@ -110,9 +110,13 @@ namespace Barotrauma
|
||||
deathChoiceInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), parent: topLeftButtonGroup.RectTransform)
|
||||
{ MaxSize = new Point(HUDLayoutSettings.ButtonAreaTop.Width / 3, int.MaxValue) }, style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Visible = false
|
||||
};
|
||||
respawnInfoText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), deathChoiceInfoFrame.RectTransform), "", wrap: true);
|
||||
respawnInfoText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), deathChoiceInfoFrame.RectTransform), "", wrap: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
deathChoiceButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), deathChoiceInfoFrame.RectTransform, Anchor.CenterRight), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = HUDLayoutSettings.Padding,
|
||||
@@ -189,7 +193,7 @@ namespace Barotrauma
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
GameMain.NetLobbyScreen.CharacterAppearanceCustomizationMenu?.AddToGUIUpdateList();
|
||||
GameMain.NetLobbyScreen?.JobSelectionFrame?.AddToGUIUpdateList();
|
||||
GameMain.NetLobbyScreen?.JobSelectionFrame?.AddToGUIUpdateList(order: 1);
|
||||
}
|
||||
|
||||
DeathPrompt?.AddToGUIUpdateList();
|
||||
@@ -282,10 +286,10 @@ namespace Barotrauma
|
||||
public void SetRespawnInfo(string text, Color textColor, bool waitForNextRoundRespawn, bool hideButtons = false)
|
||||
{
|
||||
if (topLeftButtonGroup == null) { return; }
|
||||
|
||||
|
||||
bool permadeathMode = GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath };
|
||||
bool ironmanMode = GameMain.NetworkMember is { ServerSettings: { RespawnMode: RespawnMode.Permadeath, IronmanMode: true } };
|
||||
|
||||
bool ironmanMode = GameMain.NetworkMember?.ServerSettings is { IronmanModeActive: true };
|
||||
|
||||
bool hasRespawnOptions;
|
||||
if (permadeathMode)
|
||||
{
|
||||
@@ -335,6 +339,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If there are any menu panels etc. open that contain information about the current player character, refresh it.
|
||||
/// Useful when the player character changes, e.g. at permadeath, and subsequent taking over of a bot character.
|
||||
/// </summary>
|
||||
public void RefreshAnyOpenPlayerInfo()
|
||||
{
|
||||
DebugConsole.NewMessage($"Refreshing any open player info");
|
||||
if (IsTabMenuOpen && TabMenu.SelectedTab == TabMenu.InfoFrameTab.Talents)
|
||||
{
|
||||
TabMenuInstance.SelectInfoFrameTab(TabMenu.InfoFrameTab.Talents);
|
||||
}
|
||||
// TODO: This can be expanded as need arises
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
|
||||
@@ -205,7 +205,7 @@ namespace Barotrauma
|
||||
|
||||
if (Character.Controlled.SelectedItem.GetComponent<Reactor>() is Reactor reactor && reactor.PowerOn &&
|
||||
Character.Controlled.SelectedItem.OwnInventory?.AllItems is IEnumerable<Item> containedItems &&
|
||||
containedItems.Count(i => i.HasTag(Tags.Fuel)) > 1)
|
||||
containedItems.Count(i => i.HasTag(Tags.ReactorFuel)) > 1)
|
||||
{
|
||||
if (DisplayHint("onisinteracting.reactorwithextrarods".ToIdentifier())) { return; }
|
||||
}
|
||||
@@ -316,7 +316,7 @@ namespace Barotrauma
|
||||
if (affliction?.Prefab == null) { continue; }
|
||||
if (affliction.Prefab.IsBuff) { continue; }
|
||||
if (affliction.Prefab == AfflictionPrefab.OxygenLow) { continue; }
|
||||
if (affliction.Prefab == AfflictionPrefab.RadiationSickness && (GameMain.GameSession.Map?.Radiation?.IsEntityRadiated(character) ?? false)) { continue; }
|
||||
if (affliction.Prefab == AfflictionPrefab.RadiationSickness && (GameMain.GameSession.Map?.Radiation?.DepthInRadiation(character) ?? 0) > 0) { continue; }
|
||||
if (affliction.Strength < affliction.Prefab.ShowIconThreshold) { continue; }
|
||||
DisplayHint("onafflictiondisplayed".ToIdentifier(),
|
||||
variables: new[] { ("[key]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)) },
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class PvPMode : MissionMode
|
||||
{
|
||||
private GUIComponent scoreContainer;
|
||||
private readonly GUITextBlock[] scoreTexts = new GUITextBlock[2];
|
||||
private readonly GUITextBlock[] scoreTextShadows = new GUITextBlock[2];
|
||||
private readonly int[] prevScores = new int[2];
|
||||
|
||||
private void InitUI()
|
||||
{
|
||||
scoreContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.TutorialObjectiveListArea, GUI.Canvas), childAnchor: Anchor.TopRight)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var frame = new GUIFrame(new RectTransform(new Point(scoreContainer.Rect.Width, GUI.IntScale(80)), scoreContainer.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
new GUIImage(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight), style: i == 0 ? "CoalitionIcon" : "SeparatistIcon")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
scoreTextShadows[i] = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(GUI.IntScale(38), GUI.IntScale(2)) },
|
||||
string.Empty, textColor: GUIStyle.TextColorDark, textAlignment: Alignment.CenterRight, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
scoreTexts[i] = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(GUI.IntScale(40), 0) },
|
||||
string.Empty, textAlignment: Alignment.CenterRight, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
base.AddToGUIUpdateList();
|
||||
|
||||
if (scoreContainer == null) { InitUI(); }
|
||||
|
||||
scoreContainer.Visible = false;
|
||||
foreach (var mission in Missions)
|
||||
{
|
||||
if (mission is CombatMission combatMission && combatMission.HasWinScore)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var scoreText = scoreTexts[i];
|
||||
//one team very close to the win score, start flashing the score
|
||||
if (combatMission.Scores[i] > combatMission.WinScore * 0.9f ||
|
||||
combatMission.Scores[i] == combatMission.WinScore - 1)
|
||||
{
|
||||
if (scoreText.Parent.FlashTimer <= 0.0f)
|
||||
{
|
||||
scoreText.Parent.Flash(GUIStyle.Orange);
|
||||
scoreText.Pulsate(Vector2.One, Vector2.One * 1.2f, scoreText.Parent.FlashTimer);
|
||||
}
|
||||
}
|
||||
if (prevScores[i] != combatMission.Scores[i] || scoreText.Text.IsNullOrEmpty())
|
||||
{
|
||||
scoreText.Text = scoreTextShadows[i].Text = $"{combatMission.Scores[i]}/{combatMission.WinScore}";
|
||||
scoreText.Parent.Flash(GUIStyle.Green);
|
||||
scoreText.Parent.GetAnyChild<GUIImage>().Pulsate(Vector2.One, Vector2.One * 1.2f, scoreText.Parent.FlashTimer);
|
||||
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
|
||||
}
|
||||
scoreText.Parent.RectTransform.NonScaledSize =
|
||||
new Point(
|
||||
(int)(scoreText.TextSize.X + scoreText.Padding.X + scoreText.Padding.X) + scoreText.Parent.GetChild<GUIImage>().Rect.Width + GUI.IntScale(10),
|
||||
scoreText.Parent.Rect.Height);
|
||||
scoreText.Parent.ForceLayoutRecalculation();
|
||||
prevScores[i] = combatMission.Scores[i];
|
||||
}
|
||||
scoreContainer.Visible = true;
|
||||
}
|
||||
}
|
||||
scoreContainer.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -13,11 +14,13 @@ namespace Barotrauma
|
||||
private float crewListAnimDelay = 0.25f;
|
||||
private float missionIconAnimDelay;
|
||||
|
||||
private const float jobColumnWidthPercentage = 0.11f;
|
||||
private const float characterColumnWidthPercentage = 0.44f;
|
||||
private const float statusColumnWidthPercentage = 0.45f;
|
||||
private const float JobColumnWidthPercentage = 0.05f;
|
||||
private const float CharacterColumnWidthPercentage = 0.35f;
|
||||
private const float StatusColumnWidthPercentage = 0.25f;
|
||||
private const float KillColumnWidthPercentage = 0.05f;
|
||||
private const float DeathColumnWidthPercentage = 0.05f;
|
||||
|
||||
private int jobColumnWidth, characterColumnWidth, statusColumnWidth;
|
||||
private int jobColumnWidth, characterColumnWidth, statusColumnWidth, killColumnWidth, deathColumnWidth;
|
||||
|
||||
private readonly List<Mission> selectedMissions;
|
||||
private readonly Location startLocation, endLocation;
|
||||
@@ -109,6 +112,16 @@ namespace Barotrauma
|
||||
CombatMission.GetTeamName(CharacterTeamType.Team2), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
|
||||
crewHeader2.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader2.Rect.Height * 2.0f));
|
||||
CreateCrewList(crewContent2, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID == CharacterTeamType.Team2), traitorResults);
|
||||
|
||||
if (CombatMission.Winner != CharacterTeamType.None)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewHeader.RectTransform),
|
||||
TextManager.Get(CombatMission.Winner == CharacterTeamType.Team1 ? "pvpmode.victory" : "pvpmode.defeat"), textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont,
|
||||
textColor: CombatMission.Winner == CharacterTeamType.Team1 ? GUIStyle.Green : GUIStyle.Red);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewHeader2.RectTransform),
|
||||
TextManager.Get(CombatMission.Winner == CharacterTeamType.Team2 ? "pvpmode.victory" : "pvpmode.defeat"), textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont,
|
||||
textColor: CombatMission.Winner == CharacterTeamType.Team2 ? GUIStyle.Green : GUIStyle.Red);
|
||||
}
|
||||
}
|
||||
|
||||
//header -------------------------------------------------------------------------------
|
||||
@@ -238,11 +251,12 @@ namespace Barotrauma
|
||||
textContent,
|
||||
mission.Difficulty ?? 0,
|
||||
mission.Prefab.Icon, mission.Prefab.IconColor,
|
||||
mission.GetDifficultyToolTipText(),
|
||||
out GUIImage missionIcon);
|
||||
|
||||
if (selectedMissions.Contains(mission))
|
||||
{
|
||||
UpdateMissionStateIcon(mission.Completed, missionIcon, animDelay);
|
||||
UpdateMissionStateIcon(mission is CombatMission combatMission ? CombatMission.IsInWinningTeam(GameMain.Client?.Character) : mission.Completed, missionIcon, animDelay);
|
||||
animDelay += 0.25f;
|
||||
}
|
||||
}
|
||||
@@ -429,13 +443,14 @@ namespace Barotrauma
|
||||
textContent,
|
||||
difficultyIconCount: 0,
|
||||
icon, GUIStyle.Red,
|
||||
difficultyTooltipText: null,
|
||||
out GUIImage missionIcon);
|
||||
UpdateMissionStateIcon(traitorResults.VotedCorrectTraitor, missionIcon, iconAnimDelay);
|
||||
return content;
|
||||
}
|
||||
|
||||
public static GUIComponent CreateMissionEntry(GUIComponent parent, LocalizedString header, List<LocalizedString> textContent, int difficultyIconCount,
|
||||
Sprite icon, Color iconColor, out GUIImage missionIcon)
|
||||
Sprite icon, Color iconColor, RichString difficultyTooltipText, out GUIImage missionIcon)
|
||||
{
|
||||
int spacing = GUI.IntScale(5);
|
||||
|
||||
@@ -486,7 +501,8 @@ namespace Barotrauma
|
||||
{
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Point(missionTextGroup.Rect.Width, defaultLineHeight), parent: missionTextGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = 1
|
||||
AbsoluteSpacing = 1,
|
||||
CanBeFocused = true
|
||||
};
|
||||
difficultyIndicatorGroup.RectTransform.MinSize = new Point(0, defaultLineHeight);
|
||||
var difficultyColor = Mission.GetDifficultyColor(difficultyIconCount);
|
||||
@@ -494,8 +510,8 @@ namespace Barotrauma
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), "DifficultyIndicator", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = difficultyColor
|
||||
Color = difficultyColor,
|
||||
ToolTip = difficultyTooltipText
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -567,7 +583,11 @@ namespace Barotrauma
|
||||
LocalizedString locationName = Submarine.MainSub is { AtEndExit: true } ? endLocation?.DisplayName : startLocation?.DisplayName;
|
||||
|
||||
string textTag;
|
||||
if (gameOver)
|
||||
if (gameMode is PvPMode)
|
||||
{
|
||||
textTag = "RoundSummaryRoundHasEnded";
|
||||
}
|
||||
else if (gameOver)
|
||||
{
|
||||
textTag = "RoundSummaryGameOver";
|
||||
}
|
||||
@@ -596,7 +616,14 @@ namespace Barotrauma
|
||||
textTag = "RoundSummaryReturnToEmptyLocation";
|
||||
break;
|
||||
default:
|
||||
textTag = Submarine.MainSub.AtEndExit ? "RoundSummaryProgress" : "RoundSummaryReturn";
|
||||
if (Submarine.MainSub == null)
|
||||
{
|
||||
textTag = "RoundSummaryRoundHasEnded";
|
||||
}
|
||||
else
|
||||
{
|
||||
textTag = Submarine.MainSub.AtEndExit ? "RoundSummaryProgress" : "RoundSummaryReturn";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -628,26 +655,34 @@ namespace Barotrauma
|
||||
{
|
||||
var headerFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform, Anchor.TopCenter, minSize: new Point(0, (int)(30 * GUI.Scale))) { }, isHorizontal: true)
|
||||
{
|
||||
AbsoluteSpacing = 2
|
||||
AbsoluteSpacing = 2,
|
||||
Stretch = true
|
||||
};
|
||||
GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton statusButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("label.statuslabel"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(JobColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(CharacterColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
|
||||
|
||||
float sizeMultiplier = 1.0f;
|
||||
//sizeMultiplier = (headerFrame.Rect.Width - headerFrame.AbsoluteSpacing * (headerFrame.CountChildren - 1)) / (float)headerFrame.Rect.Width;
|
||||
if (gameMode is PvPMode && GameMain.NetworkMember?.RespawnManager != null)
|
||||
{
|
||||
var killButton = new GUIButton(new RectTransform(new Vector2(KillColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("killcount"), style: "GUIButtonSmallFreeScale");
|
||||
killColumnWidth = killButton.Rect.Width;
|
||||
var deathButton = new GUIButton(new RectTransform(new Vector2(DeathColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("deathcount"), style: "GUIButtonSmallFreeScale");
|
||||
deathColumnWidth = deathButton.Rect.Width;
|
||||
}
|
||||
else
|
||||
{
|
||||
GUIButton statusButton = new GUIButton(new RectTransform(new Vector2(StatusColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("label.statuslabel"), style: "GUIButtonSmallFreeScale");
|
||||
statusColumnWidth = statusButton.Rect.Width;
|
||||
}
|
||||
|
||||
jobButton.RectTransform.RelativeSize = new Vector2(jobColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
characterButton.RectTransform.RelativeSize = new Vector2(characterColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
statusButton.RectTransform.RelativeSize = new Vector2(statusColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
|
||||
jobButton.TextBlock.Font = characterButton.TextBlock.Font = statusButton.TextBlock.Font = GUIStyle.HotkeyFont;
|
||||
jobButton.CanBeFocused = characterButton.CanBeFocused = statusButton.CanBeFocused = false;
|
||||
jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = statusButton.ForceUpperCase = ForceUpperCase.Yes;
|
||||
foreach (var btn in headerFrame.GetAllChildren<GUIButton>())
|
||||
{
|
||||
btn.TextBlock.Font = GUIStyle.HotkeyFont;
|
||||
btn.ForceUpperCase = ForceUpperCase.Yes;
|
||||
btn.CanBeFocused = false;
|
||||
}
|
||||
|
||||
jobColumnWidth = jobButton.Rect.Width;
|
||||
characterColumnWidth = characterButton.Rect.Width;
|
||||
statusColumnWidth = statusButton.Rect.Width;
|
||||
|
||||
GUIListBox crewList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform))
|
||||
{
|
||||
@@ -658,8 +693,28 @@ namespace Barotrauma
|
||||
|
||||
headerFrame.RectTransform.RelativeSize -= new Vector2(crewList.ScrollBar.RectTransform.RelativeSize.X, 0.0f);
|
||||
|
||||
killCounts.Clear();
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
foreach (CharacterInfo characterInfo in characterInfos)
|
||||
{
|
||||
if (characterInfo == null) { continue; }
|
||||
Character character = characterInfo.Character;
|
||||
Client ownerClient = GameMain.NetworkMember.ConnectedClients.FirstOrDefault(c => c.Character == character);
|
||||
int killCount = 0, deathCount = 0;
|
||||
foreach (var mission in selectedMissions)
|
||||
{
|
||||
if (mission is not CombatMission combatMission) { continue; }
|
||||
killCount += ownerClient == null ? combatMission.GetBotKillCount(characterInfo) : combatMission.GetClientKillCount(ownerClient);
|
||||
deathCount += ownerClient == null ? combatMission.GetBotDeathCount(characterInfo) : combatMission.GetClientDeathCount(ownerClient);
|
||||
}
|
||||
killCounts[characterInfo] = killCount;
|
||||
deathCounts[characterInfo] = deathCount;
|
||||
}
|
||||
}
|
||||
|
||||
float delay = crewListAnimDelay;
|
||||
foreach (CharacterInfo characterInfo in characterInfos)
|
||||
foreach (CharacterInfo characterInfo in characterInfos.OrderByDescending(ci => killCounts.GetValueOrDefault(ci)))
|
||||
{
|
||||
if (characterInfo == null) { continue; }
|
||||
CreateCharacterElement(characterInfo, crewList, traitorResults, delay);
|
||||
@@ -670,6 +725,10 @@ namespace Barotrauma
|
||||
return crewList;
|
||||
}
|
||||
|
||||
private readonly Dictionary<CharacterInfo, int> killCounts = new();
|
||||
private readonly Dictionary<CharacterInfo, int> deathCounts = new();
|
||||
|
||||
|
||||
private void CreateCharacterElement(CharacterInfo characterInfo, GUIListBox listBox, TraitorManager.TraitorResults? traitorResults, float animDelay)
|
||||
{
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, GUI.IntScale(45)), listBox.Content.RectTransform), style: "ListBoxElement")
|
||||
@@ -701,7 +760,7 @@ namespace Barotrauma
|
||||
Character character = characterInfo.Character;
|
||||
if (character == null || character.IsDead)
|
||||
{
|
||||
if (character == null && characterInfo.IsNewHire && characterInfo.CauseOfDeath == null)
|
||||
if (character == null && (characterInfo.IsNewHire || characterInfo.BotStatus == BotStatus.ActiveService) && characterInfo.CauseOfDeath == null)
|
||||
{
|
||||
statusText = TextManager.Get("CampaignCrew.NewHire");
|
||||
statusColor = GUIStyle.Blue;
|
||||
@@ -741,8 +800,21 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
GUITextBlock statusBlock = new GUITextBlock(new RectTransform(new Point(statusColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
ToolBox.LimitString(statusText.Value, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: statusColor);
|
||||
if (gameMode is PvPMode && GameMain.NetworkMember?.RespawnManager != null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Point(killColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
killCounts.GetValueOrDefault(characterInfo).ToString(), textAlignment: Alignment.Center);
|
||||
new GUITextBlock(new RectTransform(new Point(deathColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
deathCounts.GetValueOrDefault(characterInfo).ToString(), textAlignment: Alignment.Center);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUITextBlock statusBlock = new GUITextBlock(new RectTransform(new Point(statusColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
ToolBox.LimitString(statusText.Value, GUIStyle.SmallFont, statusColumnWidth), textAlignment: Alignment.Center, textColor: statusColor, font: GUIStyle.SmallFont)
|
||||
{
|
||||
ToolTip = statusText.Value
|
||||
};
|
||||
}
|
||||
|
||||
frame.FadeIn(animDelay, 0.15f);
|
||||
foreach (var child in frame.GetAllChildren())
|
||||
|
||||
Reference in New Issue
Block a user