Refactor AI considerations (#1278)

Considerations are now instantiated under a manager and re-used between entities where they pass in their blackboard to get a score back.
Also makes the API a bit nicer to use.
Also some random cleanup.

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
metalgearsloth
2020-07-08 09:37:35 +10:00
committed by GitHub
parent c25c1e1094
commit 5aefae184c
64 changed files with 625 additions and 396 deletions

View File

@@ -1,14 +1,15 @@
using System;
using System.Collections.Generic;
using Content.Server.AI.Operators;
using Content.Server.AI.Operators.Inventory;
using Content.Server.AI.Utility.Considerations;
using Content.Server.AI.Utility.Considerations.Combat.Melee;
using Content.Server.AI.Utility.Considerations.Inventory;
using Content.Server.AI.Utility.Curves;
using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States;
using Content.Server.AI.WorldState.States.Combat;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.AI.Utility.Actions.Combat.Melee
{
@@ -37,15 +38,21 @@ namespace Content.Server.AI.Utility.Actions.Combat.Melee
context.GetState<TargetEntityState>().SetValue(_entity);
}
protected override Consideration[] Considerations { get; } = {
new MeleeWeaponEquippedCon(
new InverseBoolCurve()),
new CanPutTargetInHandsCon(
new BoolCurve()),
new MeleeWeaponSpeedCon(
new QuadraticCurve(1.0f, 0.5f, 0.0f, 0.0f)),
new MeleeWeaponDamageCon(
new QuadraticCurve(1.0f, 0.25f, 0.0f, 0.0f)),
};
protected override IReadOnlyCollection<Func<float>> GetConsiderations(Blackboard context)
{
var considerationsManager = IoCManager.Resolve<ConsiderationsManager>();
return new[]
{
considerationsManager.Get<MeleeWeaponEquippedCon>()
.InverseBoolCurve(context),
considerationsManager.Get<CanPutTargetInHandsCon>()
.BoolCurve(context),
considerationsManager.Get<MeleeWeaponSpeedCon>()
.QuadraticCurve(context, 1.0f, 0.5f, 0.0f, 0.0f),
considerationsManager.Get<MeleeWeaponDamageCon>()
.QuadraticCurve(context, 1.0f, 0.25f, 0.0f, 0.0f),
};
}
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Content.Server.AI.Operators;
using Content.Server.AI.Operators.Combat;
@@ -7,7 +8,6 @@ using Content.Server.AI.Utility.Considerations;
using Content.Server.AI.Utility.Considerations.Combat;
using Content.Server.AI.Utility.Considerations.Combat.Melee;
using Content.Server.AI.Utility.Considerations.Movement;
using Content.Server.AI.Utility.Curves;
using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States;
using Content.Server.AI.WorldState.States.Combat;
@@ -15,6 +15,7 @@ using Content.Server.AI.WorldState.States.Inventory;
using Content.Server.AI.WorldState.States.Movement;
using Content.Server.GameObjects.Components.Weapon.Melee;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.AI.Utility.Actions.Combat.Melee
{
@@ -30,16 +31,15 @@ namespace Content.Server.AI.Utility.Actions.Combat.Melee
public override void SetupOperators(Blackboard context)
{
var equipped = context.GetState<EquippedEntityState>().GetValue();
MoveToEntityOperator moveOperator;
var equipped = context.GetState<EquippedEntityState>().GetValue();
if (equipped != null && equipped.TryGetComponent(out MeleeWeaponComponent meleeWeaponComponent))
{
moveOperator = new MoveToEntityOperator(Owner, _entity, meleeWeaponComponent.Range - 0.01f);
}
// I think it's possible for this to happen given planning is time-sliced?
// TODO: At this point we should abort
else
{
// TODO: Abort
moveOperator = new MoveToEntityOperator(Owner, _entity);
}
@@ -58,27 +58,28 @@ namespace Content.Server.AI.Utility.Actions.Combat.Melee
var equipped = context.GetState<EquippedEntityState>().GetValue();
context.GetState<WeaponEntityState>().SetValue(equipped);
}
protected override IReadOnlyCollection<Func<float>> GetConsiderations(Blackboard context)
{
var considerationsManager = IoCManager.Resolve<ConsiderationsManager>();
protected override Consideration[] Considerations { get; } = {
// Check if we have a weapon; easy-out
new MeleeWeaponEquippedCon(
new BoolCurve()),
// Don't attack a dead target
new TargetIsDeadCon(
new InverseBoolCurve()),
// Deprioritise a target in crit
new TargetIsCritCon(
new QuadraticCurve(-0.8f, 1.0f, 1.0f, 0.0f)),
// Somewhat prioritise distance
new DistanceCon(
new QuadraticCurve(-1.0f, 1.0f, 1.02f, 0.0f)),
// Prefer weaker targets
new TargetHealthCon(
new QuadraticCurve(1.0f, 0.4f, 0.0f, -0.02f)),
new MeleeWeaponSpeedCon(
new QuadraticCurve(1.0f, 0.5f, 0.0f, 0.0f)),
new MeleeWeaponDamageCon(
new QuadraticCurve(1.0f, 0.25f, 0.0f, 0.0f)),
};
return new[]
{
considerationsManager.Get<MeleeWeaponEquippedCon>()
.BoolCurve(context),
considerationsManager.Get<TargetIsDeadCon>()
.InverseBoolCurve(context),
considerationsManager.Get<TargetIsCritCon>()
.QuadraticCurve(context, -0.8f, 1.0f, 1.0f, 0.0f),
considerationsManager.Get<DistanceCon>()
.QuadraticCurve(context, 1.0f, 1.0f, 0.02f, 0.0f),
considerationsManager.Get<TargetHealthCon>()
.QuadraticCurve(context, 1.0f, 0.4f, 0.0f, -0.02f),
considerationsManager.Get<MeleeWeaponSpeedCon>()
.QuadraticCurve(context, 1.0f, 0.5f, 0.0f, 0.0f),
considerationsManager.Get<MeleeWeaponDamageCon>()
.QuadraticCurve(context, 1.0f, 0.25f, 0.0f, 0.0f),
};
}
}
}

View File

@@ -1,14 +1,16 @@
using System;
using System.Collections.Generic;
using Content.Server.AI.Operators.Sequences;
using Content.Server.AI.Utility.Considerations;
using Content.Server.AI.Utility.Considerations.Combat.Melee;
using Content.Server.AI.Utility.Considerations.Containers;
using Content.Server.AI.Utility.Considerations.Hands;
using Content.Server.AI.Utility.Considerations.Movement;
using Content.Server.AI.Utility.Curves;
using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States;
using Content.Server.AI.WorldState.States.Combat;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.AI.Utility.Actions.Combat.Melee
{
@@ -33,20 +35,26 @@ namespace Content.Server.AI.Utility.Actions.Combat.Melee
context.GetState<TargetEntityState>().SetValue(_entity);
context.GetState<WeaponEntityState>().SetValue(_entity);
}
protected override IReadOnlyCollection<Func<float>> GetConsiderations(Blackboard context)
{
var considerationsManager = IoCManager.Resolve<ConsiderationsManager>();
protected override Consideration[] Considerations { get; } = {
new TargetAccessibleCon(
new BoolCurve()),
new FreeHandCon(
new BoolCurve()),
new HasMeleeWeaponCon(
new InverseBoolCurve()),
new DistanceCon(
new QuadraticCurve(-1.0f, 1.0f, 1.02f, 0.0f)),
new MeleeWeaponDamageCon(
new QuadraticCurve(1.0f, 0.25f, 0.0f, 0.0f)),
new MeleeWeaponSpeedCon(
new QuadraticCurve(-1.0f, 0.5f, 1.0f, 0.0f)),
};
return new[]
{
considerationsManager.Get<TargetAccessibleCon>()
.BoolCurve(context),
considerationsManager.Get<FreeHandCon>()
.BoolCurve(context),
considerationsManager.Get<HasMeleeWeaponCon>()
.InverseBoolCurve(context),
considerationsManager.Get<DistanceCon>()
.QuadraticCurve(context, 1.0f, 1.0f, 0.02f, 0.0f),
considerationsManager.Get<MeleeWeaponDamageCon>()
.QuadraticCurve(context, 1.0f, 0.25f, 0.0f, 0.0f),
considerationsManager.Get<MeleeWeaponSpeedCon>()
.QuadraticCurve(context, -1.0f, 0.5f, 1.0f, 0.0f),
};
}
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Content.Server.AI.Operators;
using Content.Server.AI.Operators.Combat.Melee;
@@ -6,13 +7,13 @@ using Content.Server.AI.Utility.Considerations;
using Content.Server.AI.Utility.Considerations.Combat;
using Content.Server.AI.Utility.Considerations.Combat.Melee;
using Content.Server.AI.Utility.Considerations.Movement;
using Content.Server.AI.Utility.Curves;
using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States;
using Content.Server.AI.WorldState.States.Combat;
using Content.Server.AI.WorldState.States.Movement;
using Content.Server.GameObjects.Components.Weapon.Melee;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.AI.Utility.Actions.Combat.Melee
{
@@ -56,23 +57,25 @@ namespace Content.Server.AI.Utility.Actions.Combat.Melee
context.GetState<WeaponEntityState>().SetValue(Owner);
}
protected override Consideration[] Considerations { get; } = {
new CanUnarmedCombatCon(
new BoolCurve()),
// Don't attack a dead target
new TargetIsDeadCon(
new InverseBoolCurve()),
// Deprioritise a target in crit
new TargetIsCritCon(
new QuadraticCurve(-0.8f, 1.0f, 1.0f, 0.0f)),
// Somewhat prioritise distance
new DistanceCon(
new QuadraticCurve(-1.0f, 1.0f, 1.02f, 0.0f)),
// Prefer weaker targets
new TargetHealthCon(
new QuadraticCurve(1.0f, 0.4f, 0.0f, -0.02f)),
// TODO: Consider our Speed and Damage to compare this to using a weapon
// Also need to unequip our weapon if we have one (xenos can't hold one so no issue for now)
};
protected override IReadOnlyCollection<Func<float>> GetConsiderations(Blackboard context)
{
var considerationsManager = IoCManager.Resolve<ConsiderationsManager>();
return new[]
{
considerationsManager.Get<CanUnarmedCombatCon>()
.BoolCurve(context),
considerationsManager.Get<TargetIsDeadCon>()
.InverseBoolCurve(context),
considerationsManager.Get<TargetIsCritCon>()
.QuadraticCurve(context, -0.8f, 1.0f, 1.0f, 0.0f),
considerationsManager.Get<DistanceCon>()
.QuadraticCurve(context, -1.0f, 1.0f, 1.02f, 0.0f),
considerationsManager.Get<TargetHealthCon>()
.QuadraticCurve(context, 1.0f, 0.4f, 0.0f, -0.02f),
// TODO: Consider our Speed and Damage to compare this to using a weapon
// Also need to unequip our weapon if we have one (xenos can't hold one so no issue for now)
};
}
}
}