v0.14.6.0

This commit is contained in:
Joonas Rikkonen
2021-06-17 17:54:52 +03:00
parent 3f324b14e8
commit c27e2ea5ab
348 changed files with 13156 additions and 4266 deletions
@@ -0,0 +1,132 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
abstract class ShipIssueWorker
{
public const float MaxImportance = 100f;
public const float MinImportance = 0f;
public Order SuggestedOrderPrefab { get; }
private float importance;
public float Importance
{
get
{
return importance;
}
set
{
importance = MathHelper.Clamp(value, MinImportance, MaxImportance);
}
}
public float CurrentRedundancy { get; set; }
public readonly ShipCommandManager shipCommandManager;
public string Option { get; set; }
public Character OrderedCharacter { get; set; }
public Order CurrentOrder { get; private set; }
public ItemComponent TargetItemComponent { get; protected set; }
public Item TargetItem { get; protected set; }
public bool Active { get; protected set; } = true; // used to turn off the instance if errors are detected
protected virtual Character CommandingCharacter => shipCommandManager.character;
public virtual float TimeSinceLastAttempt { get; set; }
public virtual float RedundantIssueModifier => 0.5f;
public virtual bool StopDuringEmergency => true; // limit certain issue assessments when invaded by the enemies
public virtual bool AllowEasySwitching => false;
public ShipIssueWorker(ShipCommandManager shipCommandManager, Order suggestedOrderPrefab, string option = null)
{
this.shipCommandManager = shipCommandManager;
SuggestedOrderPrefab = suggestedOrderPrefab;
Option = option;
}
public void SetOrder(Character orderedCharacter)
{
OrderedCharacter = orderedCharacter;
if (orderedCharacter != CommandingCharacter)
{
CommandingCharacter.Speak(SuggestedOrderPrefab.GetChatMessage(OrderedCharacter.Name, "", false));
}
// not sure if new orders are supposed to be created each time. TODO m61: check later
CurrentOrder = new Order(SuggestedOrderPrefab, TargetItem, TargetItemComponent, CommandingCharacter);
OrderedCharacter.SetOrder(CurrentOrder, Option, priority: 3, CommandingCharacter, CommandingCharacter != OrderedCharacter);
TimeSinceLastAttempt = 0f;
}
public void RemoveOrder()
{
OrderedCharacter = null;
CurrentOrder = null;
}
protected virtual bool IsIssueViable()
{
return true;
}
public float CalculateImportance(bool isEmergency)
{
Importance = 0f; // reset anything that needs resetting
if (!Active)
{
return Importance;
}
Active = IsIssueViable();
if (isEmergency && StopDuringEmergency)
{
return Importance;
}
CalculateImportanceSpecific();
// if there are other orders of the same type already being attended to, such as fixing leaks
// reduce the relative importance of this issue
CurrentRedundancy = 1f;
foreach (ShipIssueWorker shipIssueWorker in shipCommandManager.ShipIssueWorkers)
{
if (shipIssueWorker.GetType() == GetType() && shipIssueWorker != this && shipIssueWorker.OrderAttendedTo())
{
CurrentRedundancy *= RedundantIssueModifier;
}
}
Importance *= CurrentRedundancy;
return Importance;
}
public bool OrderAttendedTo(float timeSinceLastCheck = 0f)
{
if (!HumanAIController.IsActive(OrderedCharacter))
{
return false;
}
// accept only the highest priority order
if (CurrentOrder != null && OrderedCharacter.GetCurrentOrderWithTopPriority()?.Order != CurrentOrder)
{
#if DEBUG
ShipCommandManager.ShipCommandLog($"Order {CurrentOrder.Name} did not match current order for character {OrderedCharacter} in {this}");
#endif
return false;
}
if (!shipCommandManager.AbleToTakeOrder(OrderedCharacter))
{
#if DEBUG
ShipCommandManager.ShipCommandLog(OrderedCharacter + " was unable to perform assigned order in " + this);
#endif
return false;
}
return true;
}
public abstract void CalculateImportanceSpecific();
}
}
@@ -0,0 +1,38 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
class ShipGlobalIssueFixLeaks : ShipGlobalIssue
{
readonly List<float> hullSeverities = new List<float>();
public ShipGlobalIssueFixLeaks(ShipCommandManager shipCommandManager) : base(shipCommandManager) { }
public override void CalculateGlobalIssue()
{
hullSeverities.Clear();
foreach (Gap gap in Gap.GapList)
{
if (AIObjectiveFixLeaks.IsValidTarget(gap, shipCommandManager.character))
{
hullSeverities.Add(AIObjectiveFixLeaks.GetLeakSeverity(gap));
}
}
float averagePercentage = 0f;
if (hullSeverities.Any())
{
hullSeverities.Sort();
averagePercentage = hullSeverities.TakeLast(3).Average(); // get the 3 most damaged items on the ship and get their average
}
GlobalImportance = averagePercentage;
}
}
class ShipIssueWorkerFixLeaks : ShipIssueWorkerGlobal
{
public override bool StopDuringEmergency => false;
public ShipIssueWorkerFixLeaks(ShipCommandManager shipCommandManager, Order order, ShipGlobalIssueFixLeaks shipGlobalIssueFixLeaks) : base(shipCommandManager, order, shipGlobalIssueFixLeaks) { }
}
}
@@ -0,0 +1,29 @@
namespace Barotrauma
{
abstract class ShipGlobalIssue
{
public float GlobalImportance { get; set; }
protected ShipCommandManager shipCommandManager;
public ShipGlobalIssue(ShipCommandManager shipCommandManager)
{
this.shipCommandManager = shipCommandManager;
}
public abstract void CalculateGlobalIssue();
}
abstract class ShipIssueWorkerGlobal : ShipIssueWorker
{
private readonly ShipGlobalIssue shipGlobalIssue;
public ShipIssueWorkerGlobal(ShipCommandManager shipCommandManager, Order suggestedOrderPrefab, ShipGlobalIssue shipGlobalIssue) : base (shipCommandManager, suggestedOrderPrefab)
{
this.shipGlobalIssue = shipGlobalIssue;
}
public override void CalculateImportanceSpecific() // importances for global issues are precalculated, so that they don't need to be calculated per each attending character
{
Importance = shipGlobalIssue.GlobalImportance;
}
}
}
@@ -0,0 +1,30 @@
using Barotrauma.Items.Components;
namespace Barotrauma
{
abstract class ShipIssueWorkerItem : ShipIssueWorker
{
public ShipIssueWorkerItem(ShipCommandManager shipCommandManager, Order order, Item targetItem, ItemComponent targetItemComponent, string option = null) : base(shipCommandManager, order, option)
{
TargetItemComponent = targetItemComponent;
TargetItem = targetItem;
}
protected override bool IsIssueViable()
{
if (TargetItemComponent == null)
{
DebugConsole.ThrowError("TargetItemComponent was null in " + this);
return false;
}
if (TargetItem == null)
{
DebugConsole.ThrowError("TargetItem was null in " + this);
return false;
}
return true;
}
}
}
@@ -0,0 +1,41 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
class ShipIssueWorkerOperateWeapons : ShipIssueWorkerItem
{
public override float RedundantIssueModifier => 0.65f;
private readonly List<float> targetingImportances = new List<float>();
public override bool AllowEasySwitching => true;
public ShipIssueWorkerOperateWeapons(ShipCommandManager shipCommandManager, Order order, Item targetItem, ItemComponent targetItemComponent) : base(shipCommandManager, order, targetItem, targetItemComponent) { }
float GetTargetingImportance(Entity entity)
{
float currentDistanceToEnemy = Vector2.Distance(entity.WorldPosition, TargetItem.WorldPosition);
return MathHelper.Clamp(100 - (currentDistanceToEnemy / 100f), MinImportance, MaxImportance);
}
public override void CalculateImportanceSpecific()
{
if (TargetItemComponent is Turret turret && !turret.HasPowerToShoot()) { return; }
targetingImportances.Clear();
foreach (Character character in shipCommandManager.EnemyCharacters)
{
targetingImportances.Add(GetTargetingImportance(character));
}
// there should maybe be additional logic for targeting and destroying spires, because they currently cause some issues with pathing
if (targetingImportances.Any())
{
targetingImportances.Sort();
Importance = targetingImportances.TakeLast(3).Average();
}
}
}
}
@@ -0,0 +1,21 @@
using Barotrauma.Items.Components;
namespace Barotrauma
{
class ShipIssueWorkerPowerUpReactor : ShipIssueWorkerItem
{
public ShipIssueWorkerPowerUpReactor(ShipCommandManager shipCommandManager, Order order, Item targetItem, ItemComponent targetItemComponent, string option) : base(shipCommandManager, order, targetItem, targetItemComponent, option)
{
}
public override void CalculateImportanceSpecific()
{
if (TargetItem.Condition <= 0f) { return; }
if (TargetItemComponent is Reactor reactor && -reactor.CurrPowerConsumption < float.Epsilon)
{
Importance = 40f;
}
}
}
}
@@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
class ShipGlobalIssueRepairSystems : ShipGlobalIssue
{
readonly List<Item> itemsNeedingRepair = new List<Item>();
public ShipGlobalIssueRepairSystems(ShipCommandManager shipCommandManager) : base(shipCommandManager) { }
public override void CalculateGlobalIssue()
{
itemsNeedingRepair.Clear();
foreach (Item item in shipCommandManager.CommandedSubmarine.GetItems(true))
{
if (!AIObjectiveRepairItems.ViableForRepair(item, shipCommandManager.character, shipCommandManager.character.AIController as HumanAIController)) { continue; }
if (AIObjectiveRepairItems.NearlyFullCondition(item)) { continue; }
itemsNeedingRepair.Add(item);
// merged this logic with AIObjectiveRepairItems
}
if (itemsNeedingRepair.Any())
{
itemsNeedingRepair.Sort((x, y) => y.ConditionPercentage.CompareTo(x.ConditionPercentage));
float modifiedPercentage = itemsNeedingRepair.TakeLast(3).Average(x => x.ConditionPercentage) * 0.6f + itemsNeedingRepair.TakeLast(10).Average(x => x.ConditionPercentage) * 0.4f;
// calculate a modified percentage with the most damaged items, with 60% the weight given to the top 3 damaged and the remaining given to top 10
GlobalImportance = 100 - modifiedPercentage;
}
// this system works reasonably well, though it could give extra importance to repairing critical items like reactors and junction boxes
}
}
class ShipIssueWorkerRepairSystems : ShipIssueWorkerGlobal // this class could be removed, but it might need special behavior later
{
public ShipIssueWorkerRepairSystems(ShipCommandManager shipCommandManager, Order order, ShipGlobalIssueRepairSystems shipGlobalIssueRepairSystems) : base(shipCommandManager, order, shipGlobalIssueRepairSystems)
{
}
}
}
@@ -0,0 +1,20 @@
using Barotrauma.Items.Components;
namespace Barotrauma
{
class ShipIssueWorkerSteer : ShipIssueWorkerItem
{
// The AI could be set to steer automatically through a specialized job or autonomous objectives
// but the logic involved doesn't really allow that without some annoyingly specific changes
// hence the AI will command itself to steer if steering is not being taken care of or the target location is wrong
public ShipIssueWorkerSteer(ShipCommandManager shipCommandManager, Order order, Item targetItem, ItemComponent targetItemComponent, string option) : base(shipCommandManager, order, targetItem, targetItemComponent, option) { }
public override void CalculateImportanceSpecific()
{
if (shipCommandManager.NavigationState == ShipCommandManager.NavigationStates.Inactive) { return; }
if (TargetItemComponent is Powered powered && powered.Voltage <= powered.MinVoltage) { return; }
if (TargetItem.Condition <= 0f) { return; }
Importance = 70f;
}
}
}