diff --git a/Content.Shared/_CP14/MagicRitual/CP14RitualCondition.cs b/Content.Shared/_CP14/MagicRitual/CP14RitualCondition.cs
new file mode 100644
index 0000000000..85959b87fc
--- /dev/null
+++ b/Content.Shared/_CP14/MagicRitual/CP14RitualCondition.cs
@@ -0,0 +1,10 @@
+using JetBrains.Annotations;
+
+namespace Content.Shared._CP14.MagicRitual;
+
+[ImplicitDataDefinitionForInheritors]
+[MeansImplicitUse]
+public abstract partial class CP14RitualCondition
+{
+ public abstract void Check(IEntityManager entManager, EntityUid ritual);
+}
diff --git a/Content.Shared/_CP14/MagicRitual/CP14RitualEffect.cs b/Content.Shared/_CP14/MagicRitual/CP14RitualEffect.cs
new file mode 100644
index 0000000000..a8cd1e6d25
--- /dev/null
+++ b/Content.Shared/_CP14/MagicRitual/CP14RitualEffect.cs
@@ -0,0 +1,11 @@
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._CP14.MagicRitual;
+
+[ImplicitDataDefinitionForInheritors]
+[MeansImplicitUse]
+public abstract partial class CP14RitualEffect
+{
+ public abstract void Effect(IEntityManager entMan, IPrototypeManager protoMan, EntityUid ritual);
+}
diff --git a/Content.Shared/_CP14/MagicRitual/Components/CP14RitualNodeComponent.cs b/Content.Shared/_CP14/MagicRitual/Components/CP14RitualNodeComponent.cs
new file mode 100644
index 0000000000..0a8393e145
--- /dev/null
+++ b/Content.Shared/_CP14/MagicRitual/Components/CP14RitualNodeComponent.cs
@@ -0,0 +1,25 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._CP14.MagicRitual.Components;
+
+///
+///
+///
+[RegisterComponent, NetworkedComponent]
+public sealed partial class CP14RitualNodeComponent : Component
+{
+ [DataField]
+ public List Edges = new();
+}
+
+
+[DataDefinition]
+public sealed partial class CP14RitualEdge
+{
+ [DataField(required: true)]
+ public EntProtoId TargetNode = default!;
+
+ [DataField]
+ public List Conditions = new();
+}
diff --git a/Content.Shared/_CP14/MagicRitual/Effects/ChangeNode.cs b/Content.Shared/_CP14/MagicRitual/Effects/ChangeNode.cs
new file mode 100644
index 0000000000..8e7c8b7300
--- /dev/null
+++ b/Content.Shared/_CP14/MagicRitual/Effects/ChangeNode.cs
@@ -0,0 +1,29 @@
+using System.Numerics;
+using Content.Shared._CP14.MagicRitual.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Shared._CP14.MagicRitual.Effects;
+
+public sealed partial class ChangeNode : CP14RitualEffect
+{
+ [DataField(required: true)]
+ public EntProtoId Proto = default!;
+
+ public override void Effect(IEntityManager entMan, IPrototypeManager protoMan, EntityUid ritual)
+ {
+ if (!protoMan.TryIndex(Proto, out var indexedProto))
+ return;
+
+ var compFactory = IoCManager.Resolve();
+ if (!indexedProto.TryGetComponent(out var targetNode, compFactory))
+ {
+ Logger.Error($"Ritual node {indexedProto.ID} does not have a CP14RitualNodeComponent, cannot change node.");
+ return;
+ }
+
+ var origin = entMan.GetComponent(ritual).Coordinates;
+ entMan.SpawnAtPosition(Proto, origin);
+ entMan.QueueDeleteEntity(ritual);
+ }
+}
diff --git a/Content.Shared/_CP14/MagicRitual/Effects/SpawnEntity.cs b/Content.Shared/_CP14/MagicRitual/Effects/SpawnEntity.cs
new file mode 100644
index 0000000000..19be52648c
--- /dev/null
+++ b/Content.Shared/_CP14/MagicRitual/Effects/SpawnEntity.cs
@@ -0,0 +1,29 @@
+using System.Numerics;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Shared._CP14.MagicRitual.Effects;
+
+public sealed partial class SpawnEntity : CP14RitualEffect
+{
+ [DataField(required: true)]
+ public EntProtoId Proto = default!;
+
+ [DataField]
+ public int Count = 1;
+
+ [DataField]
+ public float Offset = 1f;
+
+ public override void Effect(IEntityManager entMan, IPrototypeManager protoMan, EntityUid ritual)
+ {
+ var random = IoCManager.Resolve();
+ var origin = entMan.GetComponent(ritual).Coordinates;
+
+ for (var i = 0; i < Count; i++)
+ {
+ var spawnPosition = origin.Offset(new Vector2(random.NextFloat(-Offset, Offset), random.NextFloat(-Offset, Offset)));
+ var ent = entMan.SpawnAtPosition(Proto, spawnPosition);
+ }
+ }
+}
diff --git a/Content.Shared/_CP14/MagicRitual/Prototypes/CP14RitualGraphPrototype.cs b/Content.Shared/_CP14/MagicRitual/Prototypes/CP14RitualGraphPrototype.cs
new file mode 100644
index 0000000000..a88b71ee45
--- /dev/null
+++ b/Content.Shared/_CP14/MagicRitual/Prototypes/CP14RitualGraphPrototype.cs
@@ -0,0 +1,13 @@
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._CP14.MagicRitual.Prototypes;
+
+[Prototype("ritualGraph")]
+public sealed partial class CP14RitualGraphPrototype : IPrototype
+{
+ [IdDataField]
+ public string ID { get; private set; } = default!;
+
+ [DataField(required: true)]
+ public LocId Name = string.Empty;
+}