Unstable 0.16.0.0
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user