(d34fb2097) Major refactoring: - Replace custom logic regarding subobjectives with generic logic. - Ensure that all the objectives follow the same logic, reduce duplicate code where possible.

This commit is contained in:
Joonas Rikkonen
2019-05-16 05:32:56 +03:00
parent 05270aa165
commit a3db11876b
23 changed files with 555 additions and 865 deletions
@@ -3,6 +3,7 @@ using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -13,36 +14,31 @@ namespace Barotrauma
public override bool KeepDivingGearOn => true;
public override bool ForceRun => true;
private readonly Gap leak;
public Gap Leak { get; private set; }
private AIObjectiveFindDivingGear findDivingGear;
private AIObjectiveGetItem getWeldingTool;
private AIObjectiveContainItem refuelObjective;
private AIObjectiveGoTo gotoObjective;
private AIObjectiveOperateItem operateObjective;
public Gap Leak
{
get { return leak; }
}
public AIObjectiveFixLeak(Gap leak, Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base (character, objectiveManager, priorityModifier)
{
this.leak = leak;
Leak = leak;
}
public override bool IsCompleted()
{
return leak.Open <= 0.0f || leak.Removed;
return Leak.Open <= 0.0f || Leak.Removed;
}
public override bool CanBeCompleted => !abandon && base.CanBeCompleted;
public override float GetPriority()
{
if (leak.Open == 0.0f) { return 0.0f; }
if (Leak.Open == 0.0f) { return 0.0f; }
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
float dist = Math.Abs(character.WorldPosition.X - leak.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - leak.WorldPosition.Y) * 2.0f;
float dist = Math.Abs(character.WorldPosition.X - Leak.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - Leak.WorldPosition.Y) * 2.0f;
float distanceFactor = MathHelper.Lerp(1, 0.25f, MathUtils.InverseLerp(0, 10000, dist));
float severity = AIObjectiveFixLeaks.GetLeakSeverity(leak);
float severity = AIObjectiveFixLeaks.GetLeakSeverity(Leak);
float max = Math.Min((AIObjectiveManager.OrderPriority - 1), 90);
float devotion = Math.Min(Priority, 10) / 100;
return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + severity * distanceFactor * PriorityModifier, 0, 1));
@@ -50,58 +46,64 @@ namespace Barotrauma
public override bool IsDuplicate(AIObjective otherObjective)
{
AIObjectiveFixLeak fixLeak = otherObjective as AIObjectiveFixLeak;
if (fixLeak == null) return false;
return fixLeak.leak == leak;
if (!(otherObjective is AIObjectiveFixLeak fixLeak)) { return false; }
return fixLeak.Leak == Leak;
}
protected override void Act(float deltaTime)
{
if (!leak.IsRoomToRoom)
if (!Leak.IsRoomToRoom)
{
if (findDivingGear == null)
{
findDivingGear = new AIObjectiveFindDivingGear(character, true, objectiveManager);
AddSubObjective(findDivingGear);
}
else if (!findDivingGear.CanBeCompleted)
{
abandon = true;
return;
}
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, true, objectiveManager));
if (findDivingGear != null) { return; }
}
var weldingTool = character.Inventory.FindItemByTag("weldingtool");
if (weldingTool == null)
{
AddSubObjective(new AIObjectiveGetItem(character, "weldingtool", objectiveManager, true));
return;
TryAddSubObjective(ref getWeldingTool, () => new AIObjectiveGetItem(character, "weldingtool", objectiveManager, true));
if (getWeldingTool != null) { return; }
}
else
{
var containedItems = weldingTool.ContainedItems;
if (containedItems == null) return;
var fuelTank = containedItems.FirstOrDefault(i => i.HasTag("weldingfueltank") && i.Condition > 0.0f);
if (fuelTank == null)
if (containedItems == null)
{
AddSubObjective(new AIObjectiveContainItem(character, "weldingfueltank", weldingTool.GetComponent<ItemContainer>(), objectiveManager));
#if DEBUG
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no proper inventory");
#endif
abandon = true;
return;
}
// Drop empty tanks
foreach (Item containedItem in containedItems)
{
if (containedItem == null) { continue; }
if (containedItem.Condition <= 0.0f)
{
containedItem.Drop(character);
}
}
if (containedItems.None(i => i.HasTag("weldingfueltank") && i.Condition > 0.0f))
{
TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, "weldingfueltank", weldingTool.GetComponent<ItemContainer>(), objectiveManager));
if (refuelObjective != null) { return; }
}
}
var repairTool = weldingTool.GetComponent<RepairTool>();
if (repairTool == null) { return; }
Vector2 gapDiff = leak.WorldPosition - character.WorldPosition;
if (repairTool == null)
{
#if DEBUG
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
#endif
abandon = true;
return;
}
Vector2 gapDiff = Leak.WorldPosition - character.WorldPosition;
// TODO: use the collider size/reach?
if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
{
HumanAIController.AnimController.Crouching = true;
}
float reach = ConvertUnits.ToSimUnits(repairTool.Range);
bool canReach = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach;
if (canReach)
@@ -115,65 +117,29 @@ namespace Barotrauma
{
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
}
canReach = character.CanSeeTarget(leak, sightLimb);
canReach = character.CanSeeTarget(Leak, sightLimb);
}
else
if (!canReach)
{
if (gotoObjective != null)
{
// Check if the objective is already removed -> completed/impossible
if (!subObjectives.Contains(gotoObjective))
{
if (!gotoObjective.CanBeCompleted)
{
abandon = true;
}
gotoObjective = null;
return;
}
}
else
{
gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager)
{
CloseEnough = reach
};
if (!subObjectives.Contains(gotoObjective))
{
AddSubObjective(gotoObjective);
}
}
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager) { CloseEnough = reach });
}
if (gotoObjective == null || gotoObjective.IsCompleted())
{
if (operateObjective == null)
{
operateObjective = new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: leak);
AddSubObjective(operateObjective);
}
else if (!subObjectives.Contains(operateObjective))
{
operateObjective = null;
}
}
if (gotoObjective != null) { return; }
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak));
}
private Vector2 GetStandPosition()
{
Vector2 standPos = leak.Position;
var hull = leak.FlowTargetHull;
if (hull == null) return standPos;
if (leak.IsHorizontal)
Vector2 standPos = Leak.Position;
var hull = Leak.FlowTargetHull;
if (hull == null) { return standPos; }
if (Leak.IsHorizontal)
{
standPos += Vector2.UnitX * Math.Sign(hull.Position.X - leak.Position.X) * leak.Rect.Width;
standPos += Vector2.UnitX * Math.Sign(hull.Position.X - Leak.Position.X) * Leak.Rect.Width;
}
else
{
standPos += Vector2.UnitY * Math.Sign(hull.Position.Y - leak.Position.Y) * leak.Rect.Height;
standPos += Vector2.UnitY * Math.Sign(hull.Position.Y - Leak.Position.Y) * Leak.Rect.Height;
}
return standPos;
}
}