Sentry turrets - Part 3: Turret AI (#35058)
* Initial commit * Updated Access/command.yml * Fix for Access/AccessLevelPrototype.cs * Added silicon access levels to admin items * Included self-recharging battery changes * Revert "Included self-recharging battery changes" * Addressed reviewers comments * Additional reviewer comments
This commit is contained in:
@@ -31,7 +31,7 @@ public sealed class IdCardSystem : SharedIdCardSystem
|
|||||||
private void OnMicrowaved(EntityUid uid, IdCardComponent component, BeingMicrowavedEvent args)
|
private void OnMicrowaved(EntityUid uid, IdCardComponent component, BeingMicrowavedEvent args)
|
||||||
{
|
{
|
||||||
if (!component.CanMicrowave || !TryComp<MicrowaveComponent>(args.Microwave, out var micro) || micro.Broken)
|
if (!component.CanMicrowave || !TryComp<MicrowaveComponent>(args.Microwave, out var micro) || micro.Broken)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (TryComp<AccessComponent>(uid, out var access))
|
if (TryComp<AccessComponent>(uid, out var access))
|
||||||
{
|
{
|
||||||
@@ -78,7 +78,12 @@ public sealed class IdCardSystem : SharedIdCardSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Give them a wonderful new access to compensate for everything
|
// Give them a wonderful new access to compensate for everything
|
||||||
var random = _random.Pick(_prototypeManager.EnumeratePrototypes<AccessLevelPrototype>().ToArray());
|
var ids = _prototypeManager.EnumeratePrototypes<AccessLevelPrototype>().Where(x => x.CanAddToIdCard).ToArray();
|
||||||
|
|
||||||
|
if (ids.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var random = _random.Pick(ids);
|
||||||
|
|
||||||
access.Tags.Add(random.ID);
|
access.Tags.Add(random.ID);
|
||||||
Dirty(uid, access);
|
Dirty(uid, access);
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ public sealed partial class NPCRangedCombatComponent : Component
|
|||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public bool TargetInLOS = false;
|
public bool TargetInLOS = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, only opaque objects will block line of sight.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public bool UseOpaqueForLOSChecks = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delay after target is in LOS before we start shooting.
|
/// Delay after target is in LOS before we start shooting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public sealed partial class HTNComponent : NPCComponent
|
|||||||
/// The base task to use for planning
|
/// The base task to use for planning
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite),
|
[ViewVariables(VVAccess.ReadWrite),
|
||||||
DataField("rootTask", required: true)]
|
DataField("rootTask", required: true)]
|
||||||
public HTNCompoundTask RootTask = default!;
|
public HTNCompoundTask RootTask = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -47,4 +47,10 @@ public sealed partial class HTNComponent : NPCComponent
|
|||||||
/// Is this NPC currently planning?
|
/// Is this NPC currently planning?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables] public bool Planning => PlanningJob != null;
|
[ViewVariables] public bool Planning => PlanningJob != null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether plans should be made / updated for this entity
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool Enabled = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,39 @@ public sealed class HTNSystem : EntitySystem
|
|||||||
component.PlanningJob = null;
|
component.PlanningJob = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enable / disable the hierarchical task network of an entity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ent">The entity and its <see cref="HTNComponent"/></param>
|
||||||
|
/// <param name="state">Set 'true' to enable, or 'false' to disable, the HTN</param>
|
||||||
|
/// <param name="planCooldown">Specifies a time in seconds before the entity can start planning a new action (only takes effect when the HTN is enabled)</param>
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
[PublicAPI]
|
||||||
|
public void SetHTNEnabled(Entity<HTNComponent> ent, bool state, float planCooldown = 0f)
|
||||||
|
{
|
||||||
|
if (ent.Comp.Enabled == state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.Enabled = state;
|
||||||
|
ent.Comp.PlanAccumulator = planCooldown;
|
||||||
|
|
||||||
|
ent.Comp.PlanningToken?.Cancel();
|
||||||
|
ent.Comp.PlanningToken = null;
|
||||||
|
|
||||||
|
if (ent.Comp.Plan != null)
|
||||||
|
{
|
||||||
|
var currentOperator = ent.Comp.Plan.CurrentOperator;
|
||||||
|
|
||||||
|
ShutdownTask(currentOperator, ent.Comp.Blackboard, HTNOperatorStatus.Failed);
|
||||||
|
ShutdownPlan(ent.Comp);
|
||||||
|
|
||||||
|
ent.Comp.Plan = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent.Comp.Enabled && ent.Comp.PlanAccumulator <= 0)
|
||||||
|
RequestPlan(ent.Comp);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Forces the NPC to replan.
|
/// Forces the NPC to replan.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -147,12 +180,15 @@ public sealed class HTNSystem : EntitySystem
|
|||||||
_planQueue.Process();
|
_planQueue.Process();
|
||||||
var query = EntityQueryEnumerator<ActiveNPCComponent, HTNComponent>();
|
var query = EntityQueryEnumerator<ActiveNPCComponent, HTNComponent>();
|
||||||
|
|
||||||
while(query.MoveNext(out var uid, out _, out var comp))
|
while (query.MoveNext(out var uid, out _, out var comp))
|
||||||
{
|
{
|
||||||
// If we're over our max count or it's not MapInit then ignore the NPC.
|
// If we're over our max count or it's not MapInit then ignore the NPC.
|
||||||
if (count >= maxUpdates)
|
if (count >= maxUpdates)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (!comp.Enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (comp.PlanningJob != null)
|
if (comp.PlanningJob != null)
|
||||||
{
|
{
|
||||||
if (comp.PlanningJob.Exception != null)
|
if (comp.PlanningJob.Exception != null)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.Interaction;
|
using Content.Server.Interaction;
|
||||||
|
using Content.Shared.Physics;
|
||||||
|
|
||||||
namespace Content.Server.NPC.HTN.Preconditions;
|
namespace Content.Server.NPC.HTN.Preconditions;
|
||||||
|
|
||||||
@@ -13,6 +14,9 @@ public sealed partial class TargetInLOSPrecondition : HTNPrecondition
|
|||||||
[DataField("rangeKey")]
|
[DataField("rangeKey")]
|
||||||
public string RangeKey = "RangeKey";
|
public string RangeKey = "RangeKey";
|
||||||
|
|
||||||
|
[DataField("opaqueKey")]
|
||||||
|
public bool UseOpaqueForLOSChecksKey = true;
|
||||||
|
|
||||||
public override void Initialize(IEntitySystemManager sysManager)
|
public override void Initialize(IEntitySystemManager sysManager)
|
||||||
{
|
{
|
||||||
base.Initialize(sysManager);
|
base.Initialize(sysManager);
|
||||||
@@ -27,7 +31,8 @@ public sealed partial class TargetInLOSPrecondition : HTNPrecondition
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var range = blackboard.GetValueOrDefault<float>(RangeKey, _entManager);
|
var range = blackboard.GetValueOrDefault<float>(RangeKey, _entManager);
|
||||||
|
var collisionGroup = UseOpaqueForLOSChecksKey ? CollisionGroup.Opaque : (CollisionGroup.Impassable | CollisionGroup.InteractImpassable);
|
||||||
|
|
||||||
return _interaction.InRangeUnobstructed(owner, target, range);
|
return _interaction.InRangeUnobstructed(owner, target, range, collisionGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ public sealed partial class GunOperator : HTNOperator, IHtnConditionalShutdown
|
|||||||
[DataField("requireLOS")]
|
[DataField("requireLOS")]
|
||||||
public bool RequireLOS = false;
|
public bool RequireLOS = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, only opaque objects will block line of sight.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("opaqueKey")]
|
||||||
|
public bool UseOpaqueForLOSChecks = false;
|
||||||
|
|
||||||
// Like movement we add a component and pass it off to the dedicated system.
|
// Like movement we add a component and pass it off to the dedicated system.
|
||||||
|
|
||||||
public override async Task<(bool Valid, Dictionary<string, object>? Effects)> Plan(NPCBlackboard blackboard,
|
public override async Task<(bool Valid, Dictionary<string, object>? Effects)> Plan(NPCBlackboard blackboard,
|
||||||
@@ -56,8 +62,10 @@ public sealed partial class GunOperator : HTNOperator, IHtnConditionalShutdown
|
|||||||
public override void Startup(NPCBlackboard blackboard)
|
public override void Startup(NPCBlackboard blackboard)
|
||||||
{
|
{
|
||||||
base.Startup(blackboard);
|
base.Startup(blackboard);
|
||||||
|
|
||||||
var ranged = _entManager.EnsureComponent<NPCRangedCombatComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
|
var ranged = _entManager.EnsureComponent<NPCRangedCombatComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
|
||||||
ranged.Target = blackboard.GetValue<EntityUid>(TargetKey);
|
ranged.Target = blackboard.GetValue<EntityUid>(TargetKey);
|
||||||
|
ranged.UseOpaqueForLOSChecks = UseOpaqueForLOSChecks;
|
||||||
|
|
||||||
if (blackboard.TryGetValue<float>(NPCBlackboard.RotateSpeed, out var rotSpeed, _entManager))
|
if (blackboard.TryGetValue<float>(NPCBlackboard.RotateSpeed, out var rotSpeed, _entManager))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Content.Server.NPC.Queries.Considerations;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns 0f if the NPC has a <see cref="TurretTargetSettingsComponent"/> and the
|
||||||
|
/// target entity is exempt from being targeted, otherwise it returns 1f.
|
||||||
|
/// See <see cref="TurretTargetSettingsSystem.EntityIsTargetForTurret"/>
|
||||||
|
/// for further details on turret target validation.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class TurretTargetingCon : UtilityConsideration
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Server.NPC.Components;
|
using Content.Server.NPC.Components;
|
||||||
using Content.Shared.CombatMode;
|
using Content.Shared.CombatMode;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Physics;
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
@@ -132,8 +133,10 @@ public sealed partial class NPCCombatSystem
|
|||||||
if (comp.LOSAccumulator < 0f)
|
if (comp.LOSAccumulator < 0f)
|
||||||
{
|
{
|
||||||
comp.LOSAccumulator += UnoccludedCooldown;
|
comp.LOSAccumulator += UnoccludedCooldown;
|
||||||
|
|
||||||
// For consistency with NPC steering.
|
// For consistency with NPC steering.
|
||||||
comp.TargetInLOS = _interaction.InRangeUnobstructed(uid, comp.Target, distance + 0.1f);
|
var collisionGroup = comp.UseOpaqueForLOSChecks ? CollisionGroup.Opaque : (CollisionGroup.Impassable | CollisionGroup.InteractImpassable);
|
||||||
|
comp.TargetInLOS = _interaction.InRangeUnobstructed(uid, comp.Target, distance + 0.1f, collisionGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!comp.TargetInLOS)
|
if (!comp.TargetInLOS)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ using Content.Shared.NPC.Systems;
|
|||||||
using Content.Shared.Nutrition.Components;
|
using Content.Shared.Nutrition.Components;
|
||||||
using Content.Shared.Nutrition.EntitySystems;
|
using Content.Shared.Nutrition.EntitySystems;
|
||||||
using Content.Shared.Tools.Systems;
|
using Content.Shared.Tools.Systems;
|
||||||
|
using Content.Shared.Turrets;
|
||||||
using Content.Shared.Weapons.Melee;
|
using Content.Shared.Weapons.Melee;
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
@@ -53,6 +54,7 @@ public sealed class NPCUtilitySystem : EntitySystem
|
|||||||
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
||||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||||
[Dependency] private readonly MobThresholdSystem _thresholdSystem = default!;
|
[Dependency] private readonly MobThresholdSystem _thresholdSystem = default!;
|
||||||
|
[Dependency] private readonly TurretTargetSettingsSystem _turretTargetSettings = default!;
|
||||||
|
|
||||||
private EntityQuery<PuddleComponent> _puddleQuery;
|
private EntityQuery<PuddleComponent> _puddleQuery;
|
||||||
private EntityQuery<TransformComponent> _xformQuery;
|
private EntityQuery<TransformComponent> _xformQuery;
|
||||||
@@ -358,6 +360,14 @@ public sealed class NPCUtilitySystem : EntitySystem
|
|||||||
return 1f;
|
return 1f;
|
||||||
return 0f;
|
return 0f;
|
||||||
}
|
}
|
||||||
|
case TurretTargetingCon:
|
||||||
|
{
|
||||||
|
if (!TryComp<TurretTargetSettingsComponent>(owner, out var turretTargetSettings) ||
|
||||||
|
_turretTargetSettings.EntityIsTargetForTurret((owner, turretTargetSettings), targetUid))
|
||||||
|
return 1f;
|
||||||
|
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,15 @@ namespace Content.Shared.Access
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The player-visible name of the access level, in the ID card console and such.
|
/// The player-visible name of the access level, in the ID card console and such.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("name")]
|
[DataField]
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Denotes whether this access level is intended to be assignable to a crew ID card.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool CanAddToIdCard = true;
|
||||||
|
|
||||||
public string GetAccessLevelName()
|
public string GetAccessLevelName()
|
||||||
{
|
{
|
||||||
if (Name is { } name)
|
if (Name is { } name)
|
||||||
|
|||||||
19
Content.Shared/Turrets/TurretTargetSettingsComponent.cs
Normal file
19
Content.Shared/Turrets/TurretTargetSettingsComponent.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Shared.Access;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Turrets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attached to entities to provide them with turret target selection data.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
[Access(typeof(TurretTargetSettingsSystem))]
|
||||||
|
public sealed partial class TurretTargetSettingsComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Crew with one or more access levels from this list are exempt from being targeted by turrets.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public HashSet<ProtoId<AccessLevelPrototype>> ExemptAccessLevels = new();
|
||||||
|
}
|
||||||
126
Content.Shared/Turrets/TurretTargetSettingsSystem.cs
Normal file
126
Content.Shared/Turrets/TurretTargetSettingsSystem.cs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
using Content.Shared.Access;
|
||||||
|
using Content.Shared.Access.Systems;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Turrets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This system is used for validating potential targets for NPCs with a <see cref="TurretTargetSettingsComponent"/> (i.e., turrets).
|
||||||
|
/// A turret will consider an entity a valid target if the entity does not possess any access tags which appear on the
|
||||||
|
/// turret's <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class TurretTargetSettingsSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||||
|
|
||||||
|
private ProtoId<AccessLevelPrototype> _accessLevelBorg = "Borg";
|
||||||
|
private ProtoId<AccessLevelPrototype> _accessLevelBasicSilicon = "BasicSilicon";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or removes access levels from a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
|
||||||
|
/// <param name="exemption">The proto ID for the access level</param>
|
||||||
|
/// <param name="enabled">Set 'true' to add the exemption, or 'false' to remove it</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public void SetAccessLevelExemption(Entity<TurretTargetSettingsComponent> ent, ProtoId<AccessLevelPrototype> exemption, bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
ent.Comp.ExemptAccessLevels.Add(exemption);
|
||||||
|
else
|
||||||
|
ent.Comp.ExemptAccessLevels.Remove(exemption);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or removes a collection of access levels from a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
|
||||||
|
/// <param name="exemption">The collection of access level proto IDs to add or remove</param>
|
||||||
|
/// <param name="enabled">Set 'true' to add the collection as exemptions, or 'false' to remove them</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public void SetAccessLevelExemptions(Entity<TurretTargetSettingsComponent> ent, ICollection<ProtoId<AccessLevelPrototype>> exemptions, bool enabled)
|
||||||
|
{
|
||||||
|
foreach (var exemption in exemptions)
|
||||||
|
SetAccessLevelExemption(ent, exemption, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list to contain only a supplied collection of access levels.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
|
||||||
|
/// <param name="exemptions">The supplied collection of access level proto IDs</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public void SyncAccessLevelExemptions(Entity<TurretTargetSettingsComponent> ent, ICollection<ProtoId<AccessLevelPrototype>> exemptions)
|
||||||
|
{
|
||||||
|
ent.Comp.ExemptAccessLevels.Clear();
|
||||||
|
SetAccessLevelExemptions(ent, exemptions, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list to match that of another.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The entity this is having its exemption list updated <see cref="TurretTargetSettingsComponent"/></param>
|
||||||
|
/// <param name="source">The entity that is being used as a template for the target</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public void SyncAccessLevelExemptions(Entity<TurretTargetSettingsComponent> target, Entity<TurretTargetSettingsComponent> source)
|
||||||
|
{
|
||||||
|
SyncAccessLevelExemptions(target, source.Comp.ExemptAccessLevels);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list contains a specific access level.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
|
||||||
|
/// <param name="exemption">The access level proto ID being checked</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public bool HasAccessLevelExemption(Entity<TurretTargetSettingsComponent> ent, ProtoId<AccessLevelPrototype> exemption)
|
||||||
|
{
|
||||||
|
if (ent.Comp.ExemptAccessLevels.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ent.Comp.ExemptAccessLevels.Contains(exemption);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list contains one or more access levels from another collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
|
||||||
|
/// <param name="exemptions"></param>
|
||||||
|
[PublicAPI]
|
||||||
|
public bool HasAnyAccessLevelExemption(Entity<TurretTargetSettingsComponent> ent, ICollection<ProtoId<AccessLevelPrototype>> exemptions)
|
||||||
|
{
|
||||||
|
if (ent.Comp.ExemptAccessLevels.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var exemption in exemptions)
|
||||||
|
{
|
||||||
|
if (HasAccessLevelExemption(ent, exemption))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether an entity is a valid target for a turret.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Returns false if the target possesses one or more access tags that are present on the entity's <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
|
||||||
|
/// <param name="target">The target entity</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public bool EntityIsTargetForTurret(Entity<TurretTargetSettingsComponent> ent, EntityUid target)
|
||||||
|
{
|
||||||
|
var accessLevels = _accessReader.FindAccessTags(target);
|
||||||
|
|
||||||
|
if (accessLevels.Contains(_accessLevelBorg))
|
||||||
|
return !HasAccessLevelExemption(ent, _accessLevelBorg);
|
||||||
|
|
||||||
|
if (accessLevels.Contains(_accessLevelBasicSilicon))
|
||||||
|
return !HasAccessLevelExemption(ent, _accessLevelBasicSilicon);
|
||||||
|
|
||||||
|
return !HasAnyAccessLevelExemption(ent, accessLevels);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,3 +45,7 @@ id-card-access-level-syndicate-agent = Syndicate Agent
|
|||||||
id-card-access-level-central-command = Central Command
|
id-card-access-level-central-command = Central Command
|
||||||
|
|
||||||
id-card-access-level-wizard = Wizard
|
id-card-access-level-wizard = Wizard
|
||||||
|
|
||||||
|
id-card-access-level-station-ai = Artifical Intelligence
|
||||||
|
id-card-access-level-borg = Cyborg
|
||||||
|
id-card-access-level-basic-silicon = Robot
|
||||||
@@ -13,14 +13,20 @@
|
|||||||
- type: accessGroup
|
- type: accessGroup
|
||||||
id: Command
|
id: Command
|
||||||
tags:
|
tags:
|
||||||
- Command
|
|
||||||
- Captain
|
- Captain
|
||||||
- HeadOfPersonnel
|
- Command
|
||||||
|
- ChiefEngineer
|
||||||
|
- ChiefMedicalOfficer
|
||||||
- Cryogenics
|
- Cryogenics
|
||||||
|
- HeadOfPersonnel
|
||||||
|
- HeadOfSecurity
|
||||||
|
- Quartermaster
|
||||||
|
- ResearchDirector
|
||||||
|
|
||||||
- type: accessLevel
|
- type: accessLevel
|
||||||
id: EmergencyShuttleRepealAll
|
id: EmergencyShuttleRepealAll
|
||||||
name: id-card-access-level-emergency-shuttle-repeal
|
name: id-card-access-level-emergency-shuttle-repeal
|
||||||
|
canAddToIdCard: false
|
||||||
|
|
||||||
- type: accessLevel
|
- type: accessLevel
|
||||||
id: Cryogenics
|
id: Cryogenics
|
||||||
|
|||||||
21
Resources/Prototypes/Access/silicon.yml
Normal file
21
Resources/Prototypes/Access/silicon.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
- type: accessLevel
|
||||||
|
id: Borg
|
||||||
|
name: id-card-access-level-borg
|
||||||
|
canAddToIdCard: false
|
||||||
|
|
||||||
|
- type: accessLevel
|
||||||
|
id: BasicSilicon
|
||||||
|
name: id-card-access-level-basic-silicon
|
||||||
|
canAddToIdCard: false
|
||||||
|
|
||||||
|
- type: accessLevel
|
||||||
|
id: StationAi
|
||||||
|
name: id-card-access-level-station-ai
|
||||||
|
canAddToIdCard: false
|
||||||
|
|
||||||
|
- type: accessGroup
|
||||||
|
id: Silicon
|
||||||
|
tags:
|
||||||
|
- StationAi
|
||||||
|
- Borg
|
||||||
|
- BasicSilicon
|
||||||
@@ -290,6 +290,8 @@
|
|||||||
enabled: false
|
enabled: false
|
||||||
groups:
|
groups:
|
||||||
- AllAccess
|
- AllAccess
|
||||||
|
tags:
|
||||||
|
- Borg
|
||||||
- type: AccessReader
|
- type: AccessReader
|
||||||
access: [["Command"], ["Research"]]
|
access: [["Command"], ["Research"]]
|
||||||
- type: ShowJobIcons
|
- type: ShowJobIcons
|
||||||
|
|||||||
@@ -31,6 +31,9 @@
|
|||||||
- type: NpcFactionMember
|
- type: NpcFactionMember
|
||||||
factions:
|
factions:
|
||||||
- SimpleNeutral
|
- SimpleNeutral
|
||||||
|
- type: Access
|
||||||
|
tags:
|
||||||
|
- BasicSilicon
|
||||||
- type: IntrinsicRadioReceiver
|
- type: IntrinsicRadioReceiver
|
||||||
- type: ActiveRadio
|
- type: ActiveRadio
|
||||||
channels:
|
channels:
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
- type: Access
|
- type: Access
|
||||||
groups:
|
groups:
|
||||||
- AllAccess
|
- AllAccess
|
||||||
|
- Silicon
|
||||||
tags:
|
tags:
|
||||||
- NuclearOperative
|
- NuclearOperative
|
||||||
- SyndicateAgent
|
- SyndicateAgent
|
||||||
|
|||||||
@@ -430,6 +430,7 @@
|
|||||||
- type: Access
|
- type: Access
|
||||||
groups:
|
groups:
|
||||||
- AllAccess
|
- AllAccess
|
||||||
|
- Silicon
|
||||||
- type: Eye
|
- type: Eye
|
||||||
drawFov: false
|
drawFov: false
|
||||||
- type: Examiner
|
- type: Examiner
|
||||||
|
|||||||
@@ -827,6 +827,7 @@
|
|||||||
- type: Access
|
- type: Access
|
||||||
groups:
|
groups:
|
||||||
- AllAccess
|
- AllAccess
|
||||||
|
- Silicon
|
||||||
tags:
|
tags:
|
||||||
- CentralCommand
|
- CentralCommand
|
||||||
- NuclearOperative
|
- NuclearOperative
|
||||||
|
|||||||
@@ -90,6 +90,8 @@
|
|||||||
- Armory
|
- Armory
|
||||||
- Atmospherics
|
- Atmospherics
|
||||||
- Bar
|
- Bar
|
||||||
|
- BasicSilicon
|
||||||
|
- Borg
|
||||||
- Brig
|
- Brig
|
||||||
- Detective
|
- Detective
|
||||||
- Captain
|
- Captain
|
||||||
@@ -117,6 +119,7 @@
|
|||||||
- Salvage
|
- Salvage
|
||||||
- Security
|
- Security
|
||||||
- Service
|
- Service
|
||||||
|
- StationAi
|
||||||
- Theatre
|
- Theatre
|
||||||
- CentralCommand
|
- CentralCommand
|
||||||
- NuclearOperative
|
- NuclearOperative
|
||||||
|
|||||||
@@ -32,6 +32,39 @@
|
|||||||
- tasks:
|
- tasks:
|
||||||
- !type:HTNCompoundTask
|
- !type:HTNCompoundTask
|
||||||
task: IdleSpinCompound
|
task: IdleSpinCompound
|
||||||
|
|
||||||
|
- type: htnCompound
|
||||||
|
id: EnergyTurretCompound
|
||||||
|
branches:
|
||||||
|
- tasks:
|
||||||
|
- !type:HTNPrimitiveTask
|
||||||
|
operator: !type:UtilityOperator
|
||||||
|
proto: NearbyGunTargets
|
||||||
|
|
||||||
|
- !type:HTNPrimitiveTask
|
||||||
|
preconditions:
|
||||||
|
- !type:KeyExistsPrecondition
|
||||||
|
key: Target
|
||||||
|
- !type:TargetInRangePrecondition
|
||||||
|
targetKey: Target
|
||||||
|
# TODO: Non-scuffed
|
||||||
|
rangeKey: RangedRange
|
||||||
|
- !type:TargetInLOSPrecondition
|
||||||
|
targetKey: Target
|
||||||
|
rangeKey: RangedRange
|
||||||
|
opaqueKey: true
|
||||||
|
operator: !type:GunOperator
|
||||||
|
targetKey: Target
|
||||||
|
opaqueKey: true
|
||||||
|
services:
|
||||||
|
- !type:UtilityService
|
||||||
|
id: RangedService
|
||||||
|
proto: NearbyGunTargets
|
||||||
|
key: Target
|
||||||
|
|
||||||
|
- tasks:
|
||||||
|
- !type:HTNCompoundTask
|
||||||
|
task: IdleSpinCompound
|
||||||
|
|
||||||
- type: htnCompound
|
- type: htnCompound
|
||||||
id: SimpleRangedHostileCompound
|
id: SimpleRangedHostileCompound
|
||||||
|
|||||||
@@ -188,6 +188,10 @@
|
|||||||
considerations:
|
considerations:
|
||||||
- !type:TargetIsAliveCon
|
- !type:TargetIsAliveCon
|
||||||
curve: !type:BoolCurve
|
curve: !type:BoolCurve
|
||||||
|
- !type:TargetIsCritCon
|
||||||
|
curve: !type:InverseBoolCurve
|
||||||
|
- !type:TurretTargetingCon
|
||||||
|
curve: !type:BoolCurve
|
||||||
- !type:TargetDistanceCon
|
- !type:TargetDistanceCon
|
||||||
curve: !type:PresetCurve
|
curve: !type:PresetCurve
|
||||||
preset: TargetDistance
|
preset: TargetDistance
|
||||||
|
|||||||
Reference in New Issue
Block a user