Unstable 0.16.0.0

This commit is contained in:
Markus Isberg
2022-01-14 01:28:24 +09:00
parent d9baeaa2e1
commit 7d6421a548
237 changed files with 6430 additions and 2205 deletions
@@ -47,7 +47,9 @@ namespace Barotrauma
{
if (order.TargetEntity == null)
{
DebugConsole.ThrowError("Attempted to add an order with no target entity to CrewManager!\n" + Environment.StackTrace.CleanupStackTrace());
string message = $"Attempted to add a \"{order.Name}\" order with no target entity to CrewManager!\n{Environment.StackTrace.CleanupStackTrace()}";
DebugConsole.AddWarning(message);
GameAnalyticsManager.AddErrorEventOnce("CrewManager.AddOrder:OrderTargetEntityNull", GameAnalyticsManager.ErrorSeverity.Error, message);
return false;
}
@@ -185,6 +187,10 @@ namespace Barotrauma
public void InitRound()
{
#if CLIENT
GUIContextMenu.CurrentContextMenu = null;
#endif
characters.Clear();
List<WayPoint> spawnWaypoints = null;
@@ -437,17 +443,17 @@ namespace Barotrauma
return filteredCharacters
// 1. Prioritize those who are on the same submarine than the controlled character
.OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine)
// 2. Prioritize those who have been given the same maintenance or operate order as now issued
.ThenByDescending(c => c.CurrentOrders.Any(o =>
o.Order != null && o.Order.Identifier == order.Identifier &&
(order.Category == OrderCategory.Maintenance || order.Category == OrderCategory.Operate)))
// 2. Prioritize those who are already ordered to operate the device
.ThenByDescending(c => order.Category == OrderCategory.Operate && c.CurrentOrders.Any(o => o.Order != null && o.Order.Identifier == order.Identifier && o.Order.TargetEntity == order.TargetEntity))
// 3. Prioritize those with the appropriate job for the order
.ThenByDescending(c => order.HasAppropriateJob(c))
// 4. Prioritize bots over player controlled characters
// 4. Prioritize those who don't yet have another Operate order of the same kind (which allows quick-assigning multiple Operate orders to different characters)
.ThenByDescending(c => order.Category == OrderCategory.Operate && c.CurrentOrders.None(o => o.Order != null && o.Order.Identifier == order.Identifier))
// 5. Prioritize bots over player controlled characters
.ThenByDescending(c => c.IsBot)
// 5. Use the priority value of the current objective
// 6. Use the priority value of the current objective
.ThenBy(c => c.AIController is HumanAIController humanAI ? humanAI.ObjectiveManager.CurrentObjective?.Priority : 0)
// 6. Prioritize those with the best skill for the order
// 7. Prioritize those with the best skill for the order
.ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
}
@@ -78,10 +78,11 @@ namespace Barotrauma
//there can be no events before this time has passed during the 1st campaign round
const float FirstRoundEventDelay = 0.0f;
public enum InteractionType { None, Talk, Examine, Map, Crew, Store, Repair, Upgrade, PurchaseSub }
public enum InteractionType { None, Talk, Examine, Map, Crew, Store, Repair, Upgrade, PurchaseSub, MedicalClinic }
public readonly CargoManager CargoManager;
public UpgradeManager UpgradeManager;
public MedicalClinic MedicalClinic;
public List<Faction> Factions;
@@ -176,6 +177,7 @@ namespace Barotrauma
{
Money = InitialMoney;
CargoManager = new CargoManager(this);
MedicalClinic = new MedicalClinic(this);
}
/// <summary>
@@ -192,6 +192,37 @@ namespace Barotrauma
}
#endif
}
public static List<SubmarineInfo> GetCampaignSubs()
{
bool isSubmarineVisible(SubmarineInfo s)
=> !GameMain.NetworkMember.ServerSettings.HiddenSubs.Any(h
=> s.Name.Equals(h, StringComparison.OrdinalIgnoreCase));
List<SubmarineInfo> availableSubs =
SubmarineInfo.SavedSubmarines
.Where(s =>
s.IsCampaignCompatible
&& isSubmarineVisible(s))
.ToList();
if (!availableSubs.Any())
{
//None of the available subs were marked as campaign-compatible, just include all visible subs
availableSubs.AddRange(
SubmarineInfo.SavedSubmarines
.Where(isSubmarineVisible));
}
if (!availableSubs.Any())
{
//No subs are visible at all! Just make the selected one available
availableSubs.Add(GameMain.NetLobbyScreen.SelectedSub);
}
return availableSubs;
}
}
}
@@ -411,9 +411,9 @@ namespace Barotrauma
GameAnalyticsManager.ProgressionStatus.Start,
GameMode?.Name ?? "none");
string eventId = "StartRound:GameMode:" + (GameMode?.Name ?? "none") + ":";
string eventId = "StartRound:" + (GameMode?.Preset?.Identifier ?? "none") + ":";
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"));
GameAnalyticsManager.AddDesignEvent(eventId + "GameMode:" + (GameMode?.Name ?? "none"));
GameAnalyticsManager.AddDesignEvent(eventId + "GameMode:" + (GameMode?.Preset?.Identifier ?? "none"));
GameAnalyticsManager.AddDesignEvent(eventId + "CrewSize:" + (CrewManager?.CharacterInfos?.Count() ?? 0));
foreach (Mission mission in missions)
{
@@ -421,6 +421,17 @@ namespace Barotrauma
}
GameAnalyticsManager.AddDesignEvent(eventId + "LevelType:" + (Level.Loaded?.Type.ToString() ?? "none"));
GameAnalyticsManager.AddDesignEvent(eventId + "Biome:" + (Level.Loaded?.LevelData?.Biome?.Identifier ?? "none"));
if (GameMode is CampaignMode campaignMode)
{
if (campaignMode.Map?.Radiation != null && campaignMode.Map.Radiation.Enabled)
{
GameAnalyticsManager.AddDesignEvent(eventId + "RadiationEnabled");
}
else
{
GameAnalyticsManager.AddDesignEvent(eventId + "RadiationDisabled");
}
}
#if CLIENT
if (GameMode is CampaignMode) { SteamAchievementManager.OnBiomeDiscovered(levelData.Biome); }
@@ -457,6 +468,8 @@ namespace Barotrauma
}
}
ReadyCheck.ReadyCheckCooldown = DateTime.MinValue;
GUI.PreventPauseMenuToggle = false;
HintManager.OnRoundStarted();
@@ -895,14 +908,7 @@ namespace Barotrauma
((CampaignMode)GameMode).Save(doc.Root);
try
{
doc.SaveSafe(filePath);
}
catch (Exception e)
{
DebugConsole.ThrowError("Saving gamesession to \"" + filePath + "\" failed!", e);
}
doc.SaveSafe(filePath, throwExceptions: true);
}
/*public void Load(XElement saveElement)
@@ -0,0 +1,348 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
internal partial class MedicalClinic
{
public enum NetworkHeader
{
REQUEST_AFFLICTIONS,
REQUEST_PENDING,
ADD_PENDING,
REMOVE_PENDING,
CLEAR_PENDING,
HEAL_PENDING
}
public enum AfflictionSeverity
{
Low,
Medium,
High
}
public enum MessageFlag
{
Response, // responding to your request
Announce // responding to someone else's request
}
public enum HealRequestResult
{
Unknown, // everything is not ok
Success, // everything ok
InsufficientFunds, // not enough money
Refused // the outpost has refused to provide medical assistance
}
[NetworkSerialize]
public struct NetHealRequest : INetSerializableStruct
{
public HealRequestResult Result;
}
[NetworkSerialize]
public struct NetRemovedAffliction : INetSerializableStruct
{
public NetCrewMember CrewMember;
public NetAffliction Affliction;
}
public struct NetPendingCrew : INetSerializableStruct
{
[NetworkSerialize(ArrayMaxSize = CrewManager.MaxCrewSize)]
public NetCrewMember[] CrewMembers;
}
public struct NetAffliction : INetSerializableStruct
{
[NetworkSerialize]
public string Identifier;
[NetworkSerialize]
public ushort Strength;
[NetworkSerialize]
public ushort Price;
public AfflictionSeverity AfflictionSeverity
{
get
{
if (Prefab is null) { return AfflictionSeverity.Low; }
float normalizedStrength = Strength / Prefab.MaxStrength;
// lesser than 0.1
if (normalizedStrength <= 0.1)
{
return AfflictionSeverity.Low;
}
// between 0.1 and 0.5
if (normalizedStrength > 0.1f && normalizedStrength < 0.5f)
{
return AfflictionSeverity.Medium;
}
// greater than 0.5
return AfflictionSeverity.High;
}
}
public Affliction Affliction
{
set
{
Identifier = value.Identifier;
Strength = (ushort)Math.Ceiling(value.Strength);
Price = (ushort)(Strength * value.Prefab.HealCostMultiplier);
}
}
private AfflictionPrefab? cachedPrefab;
public AfflictionPrefab? Prefab
{
get
{
if (cachedPrefab is { } cached) { return cached; }
foreach (AfflictionPrefab prefab in AfflictionPrefab.List)
{
if (prefab.Identifier.Equals(Identifier, StringComparison.OrdinalIgnoreCase))
{
cachedPrefab = prefab;
return prefab;
}
}
return null;
}
set
{
cachedPrefab = value;
Identifier = value?.Identifier ?? string.Empty;
Strength = 0;
Price = 0;
}
}
public readonly bool AfflictionEquals(AfflictionPrefab prefab)
{
return prefab.Identifier.Equals(Identifier, StringComparison.OrdinalIgnoreCase);
}
public readonly bool AfflictionEquals(NetAffliction affliction)
{
return affliction.Identifier.Equals(Identifier, StringComparison.OrdinalIgnoreCase);
}
}
public struct NetCrewMember : INetSerializableStruct
{
[NetworkSerialize]
public int CharacterInfoID;
[NetworkSerialize]
public NetAffliction[] Afflictions;
public CharacterInfo CharacterInfo
{
set => CharacterInfoID = value.GetIdentifierUsingOriginalName();
}
public readonly CharacterInfo? FindCharacterInfo(ImmutableArray<CharacterInfo> crew)
{
foreach (CharacterInfo info in crew)
{
if (info.GetIdentifierUsingOriginalName() == CharacterInfoID)
{
return info;
}
}
return null;
}
public readonly bool CharacterEquals(NetCrewMember crewMember)
{
return crewMember.CharacterInfoID == CharacterInfoID;
}
}
private readonly CampaignMode? campaign;
public MedicalClinic(CampaignMode campaign)
{
this.campaign = campaign;
}
public readonly List<NetCrewMember> PendingHeals = new List<NetCrewMember>();
public Action? OnUpdate;
private static bool IsOutpostInCombat()
{
if (!(Level.Loaded is { Type: LevelData.LevelType.Outpost })) { return false; }
IEnumerable<Character> crew = GetCrewCharacters().Where(c => c.Character != null).Select(c => c.Character).ToImmutableHashSet();
foreach (Character npc in Character.CharacterList.Where(c => c.TeamID == CharacterTeamType.FriendlyNPC))
{
bool isInCombatWithCrew = !npc.IsInstigator && npc.AIController is HumanAIController { ObjectiveManager: { CurrentObjective: AIObjectiveCombat combatObjective } } && crew.Contains(combatObjective.Enemy);
if (isInCombatWithCrew) { return true; }
}
return false;
}
private HealRequestResult HealAllPending(bool force = false)
{
int totalCost = GetTotalCost();
if (!force)
{
if (GetMoney() < totalCost) { return HealRequestResult.InsufficientFunds; }
if (IsOutpostInCombat()) { return HealRequestResult.Refused; }
}
ImmutableArray<CharacterInfo> crew = GetCrewCharacters();
foreach (NetCrewMember crewMember in PendingHeals)
{
CharacterInfo? targetCharacter = crewMember.FindCharacterInfo(crew);
if (!(targetCharacter?.Character is { CharacterHealth: { } health })) { continue; }
foreach (NetAffliction affliction in crewMember.Afflictions)
{
health.ReduceAffliction(null, affliction.Identifier, affliction.Prefab?.MaxStrength ?? affliction.Strength);
}
}
if (campaign != null)
{
campaign.Money -= totalCost;
}
ClearPendingHeals();
return HealRequestResult.Success;
}
private void ClearPendingHeals()
{
PendingHeals.Clear();
}
private void RemovePendingAffliction(NetCrewMember crewMember, NetAffliction affliction)
{
foreach (NetCrewMember listMember in PendingHeals.ToList())
{
PendingHeals.Remove(listMember);
NetCrewMember pendingMember = listMember;
if (pendingMember.CharacterEquals(crewMember))
{
List<NetAffliction> newAfflictions = new List<NetAffliction>();
foreach (NetAffliction pendingAffliction in pendingMember.Afflictions)
{
if (pendingAffliction.AfflictionEquals(affliction)) { continue; }
newAfflictions.Add(pendingAffliction);
}
pendingMember.Afflictions = newAfflictions.ToArray();
}
if (!pendingMember.Afflictions.Any()) { continue; }
PendingHeals.Add(pendingMember);
}
}
private void InsertPendingCrewMember(NetCrewMember crewMember)
{
if (PendingHeals.FirstOrNull(m => m.CharacterEquals(crewMember)) is { } foundHeal)
{
PendingHeals.Remove(foundHeal);
}
PendingHeals.Add(crewMember);
}
private NetAffliction[] GetAllAfflictions(CharacterHealth health)
{
IEnumerable<Affliction> rawAfflictions = health.GetAllAfflictions().Where(a => !a.Prefab.IsBuff && a.Strength > GetShowTreshold(a));
List<NetAffliction> afflictions = new List<NetAffliction>();
foreach (Affliction affliction in rawAfflictions)
{
NetAffliction newAffliction;
if (afflictions.FirstOrNull(netAffliction => netAffliction.AfflictionEquals(affliction.Prefab)) is { } foundAffliction)
{
afflictions.Remove(foundAffliction);
foundAffliction.Strength += (ushort)affliction.Strength;
foundAffliction.Price += (ushort)GetAdjustedPrice((int)(affliction.Prefab.HealCostMultiplier * affliction.Strength));
newAffliction = foundAffliction;
}
else
{
newAffliction = new NetAffliction { Affliction = affliction };
newAffliction.Price = (ushort)GetAdjustedPrice(newAffliction.Price);
}
afflictions.Add(newAffliction);
}
return afflictions.ToArray();
static float GetShowTreshold(Affliction affliction) => Math.Max(0, Math.Min(affliction.Prefab.ShowIconToOthersThreshold, affliction.Prefab.ShowInHealthScannerThreshold));
}
public int GetTotalCost() => PendingHeals.SelectMany(h => h.Afflictions).Aggregate(0, (current, affliction) => current + affliction.Price);
private int GetAdjustedPrice(int price) => campaign?.Map?.CurrentLocation is { Type: { HasOutpost: true } } currentLocation ? currentLocation.GetAdjustedHealCost(price) : int.MaxValue;
public int GetMoney() => campaign?.Money ?? 0;
public static ImmutableArray<CharacterInfo> GetCrewCharacters()
{
#if DEBUG && CLIENT
if (Screen.Selected is TestScreen)
{
return TestInfos.ToImmutableArray();
}
#endif
return Character.CharacterList.Where(c => c.Info != null && c.TeamID == CharacterTeamType.Team1).Select(c => c.Info).ToImmutableArray();
}
#if DEBUG && CLIENT
private static readonly CharacterInfo[] TestInfos =
{
new CharacterInfo("human"),
new CharacterInfo("human"),
new CharacterInfo("human"),
new CharacterInfo("human"),
new CharacterInfo("human"),
new CharacterInfo("human"),
new CharacterInfo("human")
};
private static readonly NetAffliction[] TestAfflictions =
{
new NetAffliction { Identifier = "internaldamage", Strength = 80, Price = 10 },
new NetAffliction { Identifier = "blunttrauma", Strength = 50, Price = 10 },
new NetAffliction { Identifier = "lacerations", Strength = 20, Price = 10 },
new NetAffliction { Identifier = "burn", Strength = 10, Price = 10 }
};
#endif
}
}