Data-driven NPC factions (#3249)
* Data-driven NPC factions * Minor re-factor so it's settable via yaml instead. Any changes made during runtime aren't saved back. * Still uses enums given bitmasks go BRRT * private setters whoops * nullables Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.AI
|
||||||
|
{
|
||||||
|
[Prototype("aiFaction")]
|
||||||
|
public class AiFactionPrototype : IIndexedPrototype, IPrototype
|
||||||
|
{
|
||||||
|
// These are immutable so any dynamic changes aren't saved back over.
|
||||||
|
// AiFactionSystem will just read these and then store them.
|
||||||
|
|
||||||
|
public string ID { get; private set; } = default!;
|
||||||
|
|
||||||
|
public IReadOnlyList<string> Hostile { get; private set; } = default!;
|
||||||
|
|
||||||
|
public void LoadFrom(YamlMappingNode mapping)
|
||||||
|
{
|
||||||
|
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||||
|
|
||||||
|
serializer.DataField(this, x => x.ID, "id", string.Empty);
|
||||||
|
serializer.DataField(this, x => x.Hostile, "hostile", new List<string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
#nullable enable
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Content.Server.GameObjects.EntitySystems.AI;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
@@ -35,15 +37,4 @@ namespace Content.Server.GameObjects.Components.AI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum Faction
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
NanoTrasen = 1 << 0,
|
|
||||||
SimpleHostile = 1 << 1,
|
|
||||||
SimpleNeutral = 1 << 2,
|
|
||||||
Syndicate = 1 << 3,
|
|
||||||
Xeno = 1 << 4,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.GameObjects.Components.AI;
|
using Content.Server.GameObjects.Components.AI;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Log;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.EntitySystems.AI
|
namespace Content.Server.GameObjects.EntitySystems.AI
|
||||||
{
|
{
|
||||||
@@ -14,27 +19,44 @@ namespace Content.Server.GameObjects.EntitySystems.AI
|
|||||||
* This may change where specified friendly factions are listed. (e.g. to get number of friendlies in area).
|
* This may change where specified friendly factions are listed. (e.g. to get number of friendlies in area).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private readonly Dictionary<Faction, Faction> _hostileFactions = new();
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
var protoManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
|
||||||
|
foreach (var faction in protoManager.EnumeratePrototypes<AiFactionPrototype>())
|
||||||
|
{
|
||||||
|
if (Enum.TryParse(faction.ID, out Faction @enum))
|
||||||
|
{
|
||||||
|
var parsedFaction = Faction.None;
|
||||||
|
|
||||||
|
foreach (var hostile in faction.Hostile)
|
||||||
|
{
|
||||||
|
if (Enum.TryParse(hostile, out Faction parsedHostile))
|
||||||
|
{
|
||||||
|
parsedFaction |= parsedHostile;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error($"Unable to parse hostile faction {hostile} for {faction.ID}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_hostileFactions[@enum] = parsedFaction;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error($"Unable to parse AI faction {faction.ID}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Faction GetHostileFactions(Faction faction) => _hostileFactions.TryGetValue(faction, out var hostiles) ? hostiles : Faction.None;
|
public Faction GetHostileFactions(Faction faction) => _hostileFactions.TryGetValue(faction, out var hostiles) ? hostiles : Faction.None;
|
||||||
|
|
||||||
private readonly Dictionary<Faction, Faction> _hostileFactions = new()
|
|
||||||
{
|
|
||||||
{Faction.NanoTrasen,
|
|
||||||
Faction.SimpleHostile | Faction.Syndicate | Faction.Xeno},
|
|
||||||
{Faction.SimpleHostile,
|
|
||||||
Faction.NanoTrasen | Faction.Syndicate
|
|
||||||
},
|
|
||||||
// What makes a man turn neutral?
|
|
||||||
{Faction.SimpleNeutral,
|
|
||||||
Faction.None
|
|
||||||
},
|
|
||||||
{Faction.Syndicate,
|
|
||||||
Faction.NanoTrasen | Faction.SimpleHostile | Faction.Xeno},
|
|
||||||
{Faction.Xeno,
|
|
||||||
Faction.NanoTrasen | Faction.Syndicate},
|
|
||||||
};
|
|
||||||
|
|
||||||
public Faction GetFactions(IEntity entity) =>
|
public Faction GetFactions(IEntity entity) =>
|
||||||
entity.TryGetComponent(out AiFactionTagComponent factionTags)
|
entity.TryGetComponent(out AiFactionTagComponent? factionTags)
|
||||||
? factionTags.Factions
|
? factionTags.Factions
|
||||||
: Faction.None;
|
: Faction.None;
|
||||||
|
|
||||||
@@ -83,4 +105,15 @@ namespace Content.Server.GameObjects.EntitySystems.AI
|
|||||||
_hostileFactions[source] = hostileFactions;
|
_hostileFactions[source] = hostileFactions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum Faction
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
NanoTrasen = 1 << 0,
|
||||||
|
SimpleHostile = 1 << 1,
|
||||||
|
SimpleNeutral = 1 << 2,
|
||||||
|
Syndicate = 1 << 3,
|
||||||
|
Xeno = 1 << 4,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
Resources/Prototypes/ai_factions.yml
Normal file
28
Resources/Prototypes/ai_factions.yml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
- type: aiFaction
|
||||||
|
id: NanoTrasen
|
||||||
|
hostile:
|
||||||
|
- SimpleHostile
|
||||||
|
- Syndicate
|
||||||
|
- Xeno
|
||||||
|
|
||||||
|
- type: aiFaction
|
||||||
|
id: SimpleHostile
|
||||||
|
hostile:
|
||||||
|
- NanoTrasen
|
||||||
|
- Syndicate
|
||||||
|
|
||||||
|
- type: aiFaction
|
||||||
|
id: SimpleNeutral
|
||||||
|
|
||||||
|
- type: aiFaction
|
||||||
|
id: Syndicate
|
||||||
|
hostile:
|
||||||
|
- NanoTrasen
|
||||||
|
- SimpleHostile
|
||||||
|
- Xeno
|
||||||
|
|
||||||
|
- type: aiFaction
|
||||||
|
id: Xeno
|
||||||
|
hostile:
|
||||||
|
- NanoTrasen
|
||||||
|
- Syndicate
|
||||||
Reference in New Issue
Block a user