More AI fixes/improvements:

- AI characters can refuel the reactor when needed.
- AIObjectiveContainItem.CanBeCompleted returns true if the target item cannot be obtained or if the container cannot be reached.
- AI characters only weld leaks that are part of some submarine (i.e. not ruins).
This commit is contained in:
Joonas Rikkonen
2017-12-28 19:44:48 +02:00
parent d074d3d443
commit b84c965be3
7 changed files with 142 additions and 46 deletions

View File

@@ -250,7 +250,7 @@ namespace Barotrauma
var existingOrder = characterFrame.children.Find(c => c.UserData is Order);
if (existingOrder != null) characterFrame.RemoveChild(existingOrder);
var orderFrame = new GUIFrame(new Rectangle(-5, characterFrame.Rect.Height, characterFrame.Rect.Width, 30 + order.Options.Length * 15), "InnerFrame", characterFrame);
var orderFrame = new GUIFrame(new Rectangle(-5, 40, characterFrame.Rect.Width, 30 + order.Options.Length * 15), "InnerFrame", characterFrame);
/*orderFrame.OutlineColor = Color.LightGray * 0.5f;*/
orderFrame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f);
orderFrame.UserData = order;

View File

@@ -23,6 +23,7 @@
<Reactor canbeselected = "true">
<GuiFrame rect="0,0,760,460" alignment="Center" style="ItemUI"/>
<RequiredSkill name="Construction" level="30"/>
<StatusEffect type="InWater" target="This" Temperature="-500.0"/>
<StatusEffect type="OnActive" target="Contained" targetnames="Fuel Rod, Heat Absorber, Temperature Control Circuit" Condition="-0.1" />
<sound file="reactor.ogg" type="OnActive" range="2000.0" volume="FissionRate" volumemultiplier="0.02" loop="true"/>
@@ -55,8 +56,7 @@
<Item
name="Fuel Rod"
Tags="smallitem"
Tags="smallitem,reactorfuel"
price="200">
<Deconstruct time="10">
@@ -73,8 +73,7 @@
<Item
name="Incendium Fuel Rod"
Tags="smallitem"
Tags="smallitem,reactorfuel"
spritecolor="0.5,0.0,0.0,1.0">
<Deconstruct time="10">

View File

@@ -1,19 +1,27 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
namespace Barotrauma
{
class AIObjectiveContainItem: AIObjective
{
public int MinContainedAmount = 1;
private string[] itemNames;
private ItemContainer container;
bool isCompleted;
private bool isCompleted;
public bool IgnoreAlreadyContainedItems;
public Func<Item, float> GetItemPriority;
private AIObjectiveGetItem getItemObjective;
private AIObjectiveGoTo goToObjective;
public AIObjectiveContainItem(Character character, string itemName, ItemContainer container)
: this(character, new string[] { itemName }, container)
{
@@ -28,7 +36,28 @@ namespace Barotrauma
public override bool IsCompleted()
{
return isCompleted || itemNames.Any(name => container.Inventory.FindItem(name) != null);
if (isCompleted) return true;
int containedItemCount = 0;
foreach (Item item in container.Inventory.Items)
{
if (item != null && itemNames.Any(name => item.Prefab.NameMatches(name) || item.HasTag(name))) containedItemCount++;
}
return containedItemCount >= MinContainedAmount;
}
public override bool CanBeCompleted
{
get
{
if (goToObjective != null)
{
return goToObjective.CanBeCompleted;
}
return getItemObjective == null || !getItemObjective.CanBeCompleted;
}
}
public override float GetPriority(AIObjectiveManager objectiveManager)
@@ -49,9 +78,10 @@ namespace Barotrauma
var itemToContain = character.Inventory.FindItem(itemNames);
if (itemToContain == null)
{
var getItem = new AIObjectiveGetItem(character, itemNames);
getItem.IgnoreContainedItems = IgnoreAlreadyContainedItems;
AddSubObjective(getItem);
getItemObjective = new AIObjectiveGetItem(character, itemNames);
getItemObjective.GetItemPriority = GetItemPriority;
getItemObjective.IgnoreContainedItems = IgnoreAlreadyContainedItems;
AddSubObjective(getItemObjective);
return;
}
@@ -68,9 +98,10 @@ namespace Barotrauma
else
{
if (Vector2.Distance(character.Position, container.Item.Position) > container.Item.InteractDistance
&& !container.Item.IsInsideTrigger(character.Position))
&& !container.Item.IsInsideTrigger(character.WorldPosition))
{
AddSubObjective(new AIObjectiveGoTo(container.Item, character));
goToObjective = new AIObjectiveGoTo(container.Item, character);
AddSubObjective(goToObjective);
return;
}

View File

@@ -106,6 +106,9 @@ namespace Barotrauma
if (gap.ConnectedWall == null) continue;
if (gap.ConnectedDoor != null || gap.Open <= 0.0f) continue;
//TODO: prevent the AI characters from fixing leaks in the enemy sub in sub-vs-sub missions if/when multiplayer bots are implemented
if (gap.Submarine == null) continue;
float gapPriority = GetGapFixPriority(gap);
int index = 0;

View File

@@ -1,5 +1,6 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -7,6 +8,8 @@ namespace Barotrauma
{
class AIObjectiveGetItem : AIObjective
{
public Func<Item, float> GetItemPriority;
private string[] itemNames;
private Item targetItem, moveToTarget;
@@ -19,6 +22,8 @@ namespace Barotrauma
private AIObjectiveGoTo goToObjective;
private float currItemPriority;
private bool equip;
public override bool CanBeCompleted
@@ -172,8 +177,20 @@ namespace Barotrauma
if (owner != null && !owner.IsDead) continue;
}
//ignore if item is further away than the currently targeted item
if (moveToTarget != null && Vector2.DistanceSquared((rootContainer ?? item).Position, character.Position) > currDist) continue;
float itemPriority = 0.0f;
if (GetItemPriority != null)
{
//ignore if the item has zero priority
itemPriority = GetItemPriority(item);
if (itemPriority <= 0.0f) continue;
}
itemPriority = itemPriority - Vector2.Distance((rootContainer ?? item).Position, character.Position) * 0.01f;
//ignore if the item has a lower priority than the currently selected one
if (moveToTarget != null && itemPriority < currItemPriority) continue;
currItemPriority = itemPriority;
targetItem = item;
moveToTarget = rootContainer ?? item;

View File

@@ -399,7 +399,7 @@ namespace Barotrauma.Items.Components
float[] skillSuccess = new float[requiredSkills.Count];
for (int i = 0; i < requiredSkills.Count; i++ )
for (int i = 0; i < requiredSkills.Count; i++)
{
int characterLevel = character.GetSkillLevel(requiredSkills[i].Name);
@@ -408,7 +408,7 @@ namespace Barotrauma.Items.Components
float average = skillSuccess.Average();
return (average+100.0f)/2.0f;
return (average + 100.0f) / 2.0f;
}
public virtual void FlipX() { }

View File

@@ -3,6 +3,7 @@ using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
@@ -141,6 +142,9 @@ namespace Barotrauma.Items.Components
public float AvailableFuel { get; set; }
private float availableHeat, availableCooling;
private float prevTemperature, temperatureChange;
[Serialize(500.0f, true)]
public float ShutDownTemp
{
@@ -191,12 +195,20 @@ namespace Barotrauma.Items.Components
fissionRate = Math.Min(fissionRate, AvailableFuel);
float heat = 80 * fissionRate * (AvailableFuel / 2000.0f);
float heatDissipation = 50 * coolingRate + Math.Max(ExtraCooling, 5.0f);
//the amount of cooling is always non-zero, so that the reactor always needs
//to generate some amount of heat to prevent the temperature from dropping
availableCooling = Math.Max(ExtraCooling, 5.0f);
availableHeat = 80 * (AvailableFuel / 2000.0f);
float deltaTemp = (((heat - heatDissipation) * 5) - temperature) / 10000.0f;
float heat = availableHeat * fissionRate;
float heatDissipation = 50 * coolingRate + availableCooling;
float deltaTemp = (((heat - heatDissipation) * 5) - temperature) / 10000.0f;
Temperature = temperature + deltaTemp;
temperatureChange = Temperature - prevTemperature;
prevTemperature = temperature;
if (temperature > fireTemp && temperature - deltaTemp < fireTemp)
{
#if CLIENT
@@ -345,37 +357,71 @@ namespace Barotrauma.Items.Components
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
{
float degreeOfSuccess = DegreeOfSuccess(character);
//characters with insufficient skill levels don't refuel the reactor
if (degreeOfSuccess > 0.2f)
{
//remove used-up fuel from the reactor
var containedItems = item.ContainedItems;
foreach (Item item in containedItems)
{
if (item != null && item.Condition <= 0.0f)
{
item.Drop();
}
}
//the temperature is too low and not increasing even though the fission rate is high and cooling low
// -> we need more fuel
if (temperature < load * 0.5f && temperatureChange <= 0.0f && fissionRate > 0.9f && coolingRate < 0.1f)
{
var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "Fuel Rod", "reactorfuel" }, item.GetComponent<ItemContainer>());
containFuelObjective.MinContainedAmount = containedItems.Count(i => i != null && i.Prefab.NameMatches("Fuel Rod") || i.HasTag("reactorfuel")) + 1;
containFuelObjective.GetItemPriority = (Item fuelItem) =>
{
if (fuelItem.ParentInventory?.Owner is Item)
{
//don't take fuel from other reactors
if (((Item)fuelItem.ParentInventory.Owner).GetComponent<Reactor>() != null) return 0.0f;
}
return 1.0f;
};
objective.AddSubObjective(containFuelObjective);
return false;
}
}
switch (objective.Option.ToLowerInvariant())
{
case "power up":
float tempDiff = load - temperature;
{
case "power up":
float tempDiff = load - temperature;
shutDownTemp = Math.Min(load + 1000.0f, 7500.0f);
shutDownTemp = Math.Min(load + 1000.0f, 7500.0f);
//temperature too high/low
if (Math.Abs(tempDiff)>500.0f)
{
AutoTemp = false;
FissionRate += deltaTime * 100.0f * Math.Sign(tempDiff);
CoolingRate -= deltaTime * 100.0f * Math.Sign(tempDiff);
}
//temperature OK
else
{
AutoTemp = true;
}
//characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
if (Math.Abs(tempDiff) < 500.0f || degreeOfSuccess < 0.5f)
{
AutoTemp = true;
}
else
{
AutoTemp = false;
//higher skill levels make the character adjust the temperature faster
FissionRate += deltaTime * 100.0f * Math.Sign(tempDiff) * degreeOfSuccess;
CoolingRate -= deltaTime * 100.0f * Math.Sign(tempDiff) * degreeOfSuccess;
}
break;
case "shutdown":
shutDownTemp = 0.0f;
break;
}
break;
case "shutdown":
shutDownTemp = 0.0f;
break;
}
return false;
return false;
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power)
{
switch (connection.Name)