(d9829ac) v0.9.4.0
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public enum AIState { Idle, Attack, Escape, Eat }
|
||||
|
||||
abstract partial class AIController : ISteerable
|
||||
{
|
||||
public enum AIState { Idle, Attack, GoTo, Escape, Eat }
|
||||
|
||||
public bool Enabled;
|
||||
|
||||
public readonly Character Character;
|
||||
|
||||
@@ -21,7 +21,9 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// How long does it take for the ai target to fade out if not kept alive.
|
||||
/// </summary>
|
||||
public float FadeOutTime { get; private set; } = 3;
|
||||
public float FadeOutTime { get; private set; }
|
||||
|
||||
public bool Static { get; private set; }
|
||||
|
||||
public float SoundRange
|
||||
{
|
||||
@@ -128,6 +130,19 @@ namespace Barotrauma
|
||||
MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
|
||||
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
|
||||
FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime);
|
||||
Static = element.GetAttributeBool("static", Static);
|
||||
if (Static)
|
||||
{
|
||||
SightRange = MaxSightRange;
|
||||
SoundRange = MaxSoundRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-static ai targets must be kept alive by a custom logic (e.g. item components)
|
||||
SightRange = MinSightRange;
|
||||
SoundRange = MinSoundRange;
|
||||
}
|
||||
|
||||
SonarDisruption = element.GetAttributeFloat("sonardisruption", 0.0f);
|
||||
SonarLabel = element.GetAttributeString("sonarlabel", "");
|
||||
string typeString = element.GetAttributeString("type", "Any");
|
||||
@@ -143,6 +158,16 @@ namespace Barotrauma
|
||||
List.Add(this);
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (!Static && FadeOutTime > 0)
|
||||
{
|
||||
// The aitarget goes silent/invisible if the components don't keep it active
|
||||
SightRange -= deltaTime * (MaxSightRange / FadeOutTime);
|
||||
SoundRange -= deltaTime * (MaxSoundRange / FadeOutTime);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWithinSector(Vector2 worldPosition)
|
||||
{
|
||||
if (sectorRad >= MathHelper.TwoPi) return true;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -69,6 +69,10 @@ namespace Barotrauma
|
||||
|
||||
public HumanAIController(Character c) : base(c)
|
||||
{
|
||||
if (!c.IsHuman)
|
||||
{
|
||||
throw new System.Exception($"Tried to create a human ai controller for a non-human: {c.SpeciesName}!");
|
||||
}
|
||||
insideSteering = new IndoorsSteeringManager(this, true, false);
|
||||
outsideSteering = new SteeringManager(this);
|
||||
objectiveManager = new AIObjectiveManager(c);
|
||||
@@ -324,7 +328,7 @@ namespace Barotrauma
|
||||
AddTargets<AIObjectiveFightIntruders, Character>(Character, c);
|
||||
if (newOrder == null)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportintruders");
|
||||
var orderPrefab = Order.GetPrefab("reportintruders");
|
||||
newOrder = new Order(orderPrefab, c.CurrentHull, null, orderGiver: Character);
|
||||
}
|
||||
}
|
||||
@@ -334,7 +338,7 @@ namespace Barotrauma
|
||||
AddTargets<AIObjectiveExtinguishFires, Hull>(Character, hull);
|
||||
if (newOrder == null)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportfire");
|
||||
var orderPrefab = Order.GetPrefab("reportfire");
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
}
|
||||
}
|
||||
@@ -347,7 +351,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (newOrder == null)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "requestfirstaid");
|
||||
var orderPrefab = Order.GetPrefab("requestfirstaid");
|
||||
newOrder = new Order(orderPrefab, c.CurrentHull, null, orderGiver: Character);
|
||||
}
|
||||
}
|
||||
@@ -360,7 +364,7 @@ namespace Barotrauma
|
||||
AddTargets<AIObjectiveFixLeaks, Gap>(Character, gap);
|
||||
if (newOrder == null && !gap.IsRoomToRoom)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportbreach");
|
||||
var orderPrefab = Order.GetPrefab("reportbreach");
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
}
|
||||
}
|
||||
@@ -374,7 +378,7 @@ namespace Barotrauma
|
||||
AddTargets<AIObjectiveRepairItems, Item>(Character, item);
|
||||
if (newOrder == null)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportbrokendevices");
|
||||
var orderPrefab = Order.GetPrefab("reportbrokendevices");
|
||||
newOrder = new Order(orderPrefab, item.CurrentHull, item.Repairables?.FirstOrDefault(), orderGiver: Character);
|
||||
}
|
||||
}
|
||||
@@ -518,11 +522,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (ObjectiveManager.CurrentOrder is AIObjectiveRescueAll rescueAll && rescueAll.Targets.None())
|
||||
{
|
||||
//TODO: re-enable on all languages after DialogNoRescueTargets has been translated
|
||||
if (TextManager.Language == "English")
|
||||
{
|
||||
Character.Speak(TextManager.Get("DialogNoRescueTargets"), null, 3.0f, "norescuetargets");
|
||||
}
|
||||
Character.Speak(TextManager.Get("DialogNoRescueTargets"), null, 3.0f, "norescuetargets");
|
||||
}
|
||||
else if (ObjectiveManager.CurrentOrder is AIObjectivePumpWater pumpWater && pumpWater.Targets.None())
|
||||
{
|
||||
@@ -620,7 +620,7 @@ namespace Barotrauma
|
||||
|
||||
public static void RefreshTargets(Character character, Order order, Hull hull)
|
||||
{
|
||||
switch (order.AITag)
|
||||
switch (order.Identifier)
|
||||
{
|
||||
case "reportfire":
|
||||
AddTargets<AIObjectiveExtinguishFires, Hull>(character, hull);
|
||||
@@ -667,7 +667,7 @@ namespace Barotrauma
|
||||
break;
|
||||
default:
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(order.AITag + " not implemented!");
|
||||
DebugConsole.ThrowError(order.Identifier + " not implemented!");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -765,6 +765,9 @@ namespace Barotrauma
|
||||
|
||||
public bool IsFriendly(Character other) => IsFriendly(Character, other);
|
||||
|
||||
public static bool IsFriendly(Character me, Character other) => (other.TeamID == me.TeamID || other.TeamID == Character.TeamType.FriendlyNPC || me.TeamID == Character.TeamType.FriendlyNPC) && other.SpeciesName == me.SpeciesName;
|
||||
public static bool IsFriendly(Character me, Character other) =>
|
||||
(other.TeamID == me.TeamID ||
|
||||
other.TeamID == Character.TeamType.FriendlyNPC ||
|
||||
me.TeamID == Character.TeamType.FriendlyNPC) && (other.SpeciesName == me.SpeciesName || other.Params.CompareGroup(me.Params.Group));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Barotrauma
|
||||
|
||||
switch (enemyAI.State)
|
||||
{
|
||||
case AIController.AIState.Idle:
|
||||
case AIState.Idle:
|
||||
if (attachToWalls && character.Submarine == null && Level.Loaded != null)
|
||||
{
|
||||
raycastTimer -= deltaTime;
|
||||
@@ -187,7 +187,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AIController.AIState.Attack:
|
||||
case AIState.Attack:
|
||||
if (enemyAI.AttackingLimb != null)
|
||||
{
|
||||
if (attachToSub && !enemyAI.IsSteeringThroughGap && wallAttachPos != Vector2.Zero && attachTargetBody != null)
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Path.GetExtension(filePath) == ".csv") continue; // .csv files are not supported
|
||||
XDocument doc = XMLExtensions.TryLoadXml(filePath);
|
||||
if (doc == null || doc.Root == null) continue;
|
||||
if (doc == null) { continue; }
|
||||
string language = doc.Root.GetAttributeString("Language", "English");
|
||||
string identifier = doc.Root.GetAttributeString("Identifier", "unknown");
|
||||
contentPackageFiles.Add(new Tuple<string, string, string>(language, identifier, filePath));
|
||||
@@ -44,7 +44,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Path.GetExtension(filePath) == ".csv") continue; // .csv files are not supported
|
||||
XDocument doc = XMLExtensions.TryLoadXml(filePath);
|
||||
if (doc == null || doc.Root == null) continue;
|
||||
if (doc == null) { continue; }
|
||||
string language = doc.Root.GetAttributeString("Language", "English");
|
||||
string identifier = doc.Root.GetAttributeString("Identifier", "unknown");
|
||||
translationFiles.Add(new Tuple<string, string, string>(language, identifier, filePath));
|
||||
@@ -73,7 +73,7 @@ namespace Barotrauma
|
||||
private static void Load(string file)
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file);
|
||||
if (doc == null || doc.Root == null) return;
|
||||
if (doc == null) { return; }
|
||||
|
||||
string language = doc.Root.GetAttributeString("Language", "English");
|
||||
if (language != TextManager.Language) return;
|
||||
@@ -102,8 +102,10 @@ namespace Barotrauma
|
||||
string allowedJobsStr = element.GetAttributeString("allowedjobs", "");
|
||||
foreach (string allowedJobIdentifier in allowedJobsStr.Split(','))
|
||||
{
|
||||
var jobPrefab = JobPrefab.List.Find(jp => jp.Identifier.ToLowerInvariant() == allowedJobIdentifier.ToLowerInvariant());
|
||||
if (jobPrefab != null) AllowedJobs.Add(jobPrefab);
|
||||
if (JobPrefab.List.TryGetValue(allowedJobIdentifier.ToLowerInvariant(), out JobPrefab jobPrefab))
|
||||
{
|
||||
AllowedJobs.Add(jobPrefab);
|
||||
}
|
||||
}
|
||||
|
||||
Flags = new List<string>(element.GetAttributeStringArray("flags", new string[0]));
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(container.Item, character, objectiveManager));
|
||||
return;
|
||||
}
|
||||
container.Combine(itemToContain);
|
||||
container.Combine(itemToContain, character);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,13 +68,13 @@ namespace Barotrauma
|
||||
public void CreateAutonomousObjectives()
|
||||
{
|
||||
Objectives.Clear();
|
||||
AddObjective(new AIObjectiveFindSafety(character, this), delay: Rand.Value() / 2);
|
||||
AddObjective(new AIObjectiveIdle(character, this), delay: Rand.Value() / 2);
|
||||
AddObjective(new AIObjectiveFindSafety(character, this));
|
||||
AddObjective(new AIObjectiveIdle(character, this));
|
||||
int objectiveCount = Objectives.Count;
|
||||
foreach (var automaticOrder in character.Info.Job.Prefab.AutomaticOrders)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == automaticOrder.aiTag);
|
||||
if (orderPrefab == null) { throw new Exception("Could not find a matching prefab by ai tag: " + automaticOrder.aiTag); }
|
||||
var orderPrefab = Order.GetPrefab(automaticOrder.identifier);
|
||||
if (orderPrefab == null) { throw new Exception($"Could not find a matching prefab by the identifier: '{automaticOrder.identifier}'"); }
|
||||
// TODO: Similar code is used in CrewManager:815-> DRY
|
||||
var matchingItems = orderPrefab.ItemIdentifiers.Any() ?
|
||||
Item.ItemList.FindAll(it => orderPrefab.ItemIdentifiers.Contains(it.Prefab.Identifier) || it.HasTag(orderPrefab.ItemIdentifiers)) :
|
||||
@@ -144,7 +144,7 @@ namespace Barotrauma
|
||||
if (previousObjective != CurrentObjective)
|
||||
{
|
||||
CurrentObjective?.OnSelected();
|
||||
GetObjective<AIObjectiveIdle>()?.SetRandom();
|
||||
GetObjective<AIObjectiveIdle>().SetRandom();
|
||||
}
|
||||
return CurrentObjective;
|
||||
}
|
||||
@@ -231,7 +231,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (order == null) { return null; }
|
||||
AIObjective newObjective;
|
||||
switch (order.AITag.ToLowerInvariant())
|
||||
switch (order.Identifier.ToLowerInvariant())
|
||||
{
|
||||
case "follow":
|
||||
if (orderGiver == null) { return null; }
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (target.Bleeding < 1 && target.Vitality / target.MaxVitality > vitalityThreshold) { return false; }
|
||||
}
|
||||
if (target.Submarine == null) { return false; }
|
||||
if (target.Submarine == null || character.Submarine == null) { return false; }
|
||||
if (target.Submarine.TeamID != character.Submarine.TeamID) { return false; }
|
||||
if (target.CurrentHull == null) { return false; }
|
||||
if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(target.CurrentHull, true)) { return false; }
|
||||
|
||||
@@ -10,9 +10,16 @@ namespace Barotrauma
|
||||
{
|
||||
class Order
|
||||
{
|
||||
private static string ConfigFile = Path.Combine("Content", "Orders.xml");
|
||||
|
||||
public static List<Order> PrefabList;
|
||||
public static Dictionary<string, Order> Prefabs { get; private set; }
|
||||
public static List<Order> PrefabList { get; private set; }
|
||||
public static Order GetPrefab(string identifier)
|
||||
{
|
||||
if (!Prefabs.TryGetValue(identifier, out Order order))
|
||||
{
|
||||
DebugConsole.ThrowError($"Cannot find an order with the identifier '{identifier}'!");
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
public Order Prefab
|
||||
{
|
||||
@@ -27,7 +34,7 @@ namespace Barotrauma
|
||||
public readonly Type ItemComponentType;
|
||||
public readonly string[] ItemIdentifiers;
|
||||
|
||||
public readonly string AITag;
|
||||
public readonly string Identifier;
|
||||
|
||||
public readonly Color Color;
|
||||
|
||||
@@ -43,30 +50,64 @@ namespace Barotrauma
|
||||
|
||||
public Character OrderGiver;
|
||||
|
||||
//legacy support
|
||||
public readonly string[] AppropriateJobs;
|
||||
public readonly string[] Options;
|
||||
public readonly string[] OptionNames;
|
||||
|
||||
static Order()
|
||||
{
|
||||
PrefabList = new List<Order>();
|
||||
Prefabs = new Dictionary<string, Order>();
|
||||
|
||||
XDocument doc = XMLExtensions.TryLoadXml(ConfigFile);
|
||||
if (doc == null || doc.Root == null) return;
|
||||
|
||||
foreach (XElement orderElement in doc.Root.Elements())
|
||||
foreach (string file in GameMain.Instance.GetFilesOfType(ContentType.Orders))
|
||||
{
|
||||
if (orderElement.Name.ToString().ToLowerInvariant() != "order") continue;
|
||||
var newOrder = new Order(orderElement);
|
||||
newOrder.Prefab = newOrder;
|
||||
PrefabList.Add(newOrder);
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file);
|
||||
if (doc == null) { continue; }
|
||||
var mainElement = doc.Root;
|
||||
bool allowOverriding = false;
|
||||
if (doc.Root.IsOverride())
|
||||
{
|
||||
mainElement = doc.Root.FirstElement();
|
||||
allowOverriding = true;
|
||||
}
|
||||
foreach (XElement sourceElement in mainElement.Elements())
|
||||
{
|
||||
var orderElement = sourceElement.IsOverride() ? sourceElement.FirstElement() : sourceElement;
|
||||
string name = orderElement.Name.ToString();
|
||||
if (name.Equals("order", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string identifier = orderElement.GetAttributeString("identifier", null);
|
||||
if (string.IsNullOrWhiteSpace(identifier))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in file {file}: The order element '{name}' does not have an identifier! All orders must have a unique identifier.");
|
||||
continue;
|
||||
}
|
||||
if (Prefabs.TryGetValue(identifier, out Order duplicate))
|
||||
{
|
||||
if (allowOverriding || sourceElement.IsOverride())
|
||||
{
|
||||
DebugConsole.NewMessage($"Overriding an existing order '{identifier}' with another one defined in '{file}'", Color.Yellow);
|
||||
Prefabs.Remove(identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in file {file}: Duplicate element with the idenfitier '{identifier}' found in '{file}'! All orders must have a unique identifier. Use <override></override> tags to override an order with the same identifier.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var newOrder = new Order(orderElement);
|
||||
newOrder.Prefab = newOrder;
|
||||
Prefabs.Add(identifier, newOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
PrefabList = new List<Order>(Prefabs.Values);
|
||||
}
|
||||
|
||||
private Order(XElement orderElement)
|
||||
{
|
||||
AITag = orderElement.GetAttributeString("aitag", "");
|
||||
Name = TextManager.Get("OrderName." + AITag, true) ?? "Name not found";
|
||||
Identifier = orderElement.GetAttributeString("identifier", "");
|
||||
Name = TextManager.Get("OrderName." + Identifier, true) ?? "Name not found";
|
||||
|
||||
string targetItemType = orderElement.GetAttributeString("targetitemtype", "");
|
||||
if (!string.IsNullOrWhiteSpace(targetItemType))
|
||||
@@ -78,7 +119,7 @@ namespace Barotrauma
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + ConfigFile + ", item component type " + targetItemType + " not found", e);
|
||||
DebugConsole.ThrowError("Error in the order definitions: item component type " + targetItemType + " not found", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +131,7 @@ namespace Barotrauma
|
||||
AppropriateJobs = orderElement.GetAttributeStringArray("appropriatejobs", new string[0]);
|
||||
Options = orderElement.GetAttributeStringArray("options", new string[0]);
|
||||
|
||||
string translatedOptionNames = TextManager.Get("OrderOptions." + AITag, true);
|
||||
string translatedOptionNames = TextManager.Get("OrderOptions." + Identifier, true);
|
||||
if (translatedOptionNames == null)
|
||||
{
|
||||
OptionNames = orderElement.GetAttributeStringArray("optionnames", new string[0]);
|
||||
@@ -116,7 +157,7 @@ namespace Barotrauma
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "sprite":
|
||||
SymbolSprite = new Sprite(subElement);
|
||||
SymbolSprite = new Sprite(subElement, lazyLoad: true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -127,7 +168,7 @@ namespace Barotrauma
|
||||
Prefab = prefab;
|
||||
|
||||
Name = prefab.Name;
|
||||
AITag = prefab.AITag;
|
||||
Identifier = prefab.Identifier;
|
||||
ItemComponentType = prefab.ItemComponentType;
|
||||
Options = prefab.Options;
|
||||
SymbolSprite = prefab.SymbolSprite;
|
||||
@@ -155,8 +196,14 @@ namespace Barotrauma
|
||||
|
||||
public bool HasAppropriateJob(Character character)
|
||||
{
|
||||
if (AppropriateJobs == null || AppropriateJobs.Length == 0) return true;
|
||||
if (character.Info == null || character.Info.Job == null) return false;
|
||||
if (character.Info == null || character.Info.Job == null) { return false; }
|
||||
if (character.Info.Job.Prefab.AppropriateOrders.Any(appropriateOrderId => Identifier == appropriateOrderId)) { return true; }
|
||||
|
||||
if (!JobPrefab.List.Values.Any(jp => jp.AppropriateOrders.Contains(Identifier)) &&
|
||||
(AppropriateJobs == null || AppropriateJobs.Length == 0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < AppropriateJobs.Length; i++)
|
||||
{
|
||||
if (character.Info.Job.Prefab.Identifier.ToLowerInvariant() == AppropriateJobs[i].ToLowerInvariant()) return true;
|
||||
@@ -168,7 +215,7 @@ namespace Barotrauma
|
||||
{
|
||||
orderOption = orderOption ?? "";
|
||||
|
||||
string messageTag = (givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf." : "OrderDialog.") + AITag;
|
||||
string messageTag = (givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf." : "OrderDialog.") + Identifier;
|
||||
if (!string.IsNullOrEmpty(orderOption)) messageTag += "." + orderOption;
|
||||
|
||||
if (targetCharacterName == null) targetCharacterName = "";
|
||||
|
||||
Reference in New Issue
Block a user