One Ring to Find Them
MMMH, REAL CODE
This RUNS! Playful premises aside, this is a functioning showcase of fennecs principles.
Get comfy, grab a cup of Java CoffeeScript Visual J# whatever, and get your paws dirty playing around in the code! It's good fun!
All .csproj
and .cs
files are over here on Github!
Premise
In this example, we'll recreate the forging of the Rings of Power in the Land of Mordor, as told in the legendary story.
We'll use the Object Link system to model the binding relationship between the One Ring and the other Rings it rules.
First, we group spawn Entities for each of the other Rings and its Bearer, and link all of them to our singleton instance of the One Ring.
Then, we bind them all in darkness with a Stream<>.For
runner.
Recipe
using fennecs;
if (!Console.IsOutputRedirected) Console.Clear();
var world = new World();
world.Entity() // Three Rings for the Elven-kings under the sky,
.Add(new RingBearer("elven"))
.Add(Link.With(OneRing.Instance)) // One Ring to rule them all,
.Spawn(3);
world.Entity() //Seven for the Dwarf-lords in their halls of stone,
.Add(new RingBearer("dwarven"))
.Add(Link.With(OneRing.Instance)) // One Ring to find them,
.Spawn(7);
world.Entity() //Nine for Mortal Men doomed to die...
.Add(new RingBearer("human")) // ("Human Rings"? I like it!)
.Add(Link.With(OneRing.Instance)) // One Ring to bring them all,
.Spawn(9);
// Use our Palantir to find all Rings linked to the One Ring
var ringsOfPower = world
.Query<RingBearer, OneRing>(Match.Plain, Link.Any)
.Has(Link.With(OneRing.Instance)) // and in the darkness bind them.
.Stream();
// Use the Query to corrupt the Ring Bearers
ringsOfPower.For((in Entity ring, ref RingBearer bearer, ref OneRing link) =>
{
bearer = bearer with { corrupted = true };
link.CallOut(ring, bearer); // it calls out to its master!
});
Console.WriteLine("\nDirected by: Peter Foxen");
// The Ring Bearer component represents the owner of a Ring of Power.
internal record struct RingBearer(string race, bool corrupted = false);
//But they were, all of them, deceived, for another ring was made!
internal class OneRing
{
// Of course it's a GoF singleton! It's straight out of Mordor!
public static readonly OneRing Instance = new();
// No, we can't "make another", precious.
private OneRing() { }
// Sample interaction for linked Entities to use
public void CallOut(Entity ring, RingBearer bearer)
{
if (bearer.corrupted)
Console.WriteLine($"{ring} corrupted its {bearer.race} bearer!");
}
}
E-00000001:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its elven bearer!
E-00000002:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its elven bearer!
E-00000003:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its elven bearer!
E-00000004:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its dwarven bearer!
E-00000005:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its dwarven bearer!
E-00000006:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its dwarven bearer!
E-00000007:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its dwarven bearer!
E-00000008:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its dwarven bearer!
E-00000009:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its dwarven bearer!
E-0000000a:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its dwarven bearer!
E-0000000b:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its human bearer!
E-0000000c:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its human bearer!
E-0000000d:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its human bearer!
E-0000000e:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its human bearer!
E-0000000f:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its human bearer!
E-00000010:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its human bearer!
E-00000011:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its human bearer!
E-00000012:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its human bearer!
E-00000013:00001 <fennecs.Identity>
|-<RingBearer>
|-<OneRing> >> O-<OneRing>#0202C666 corrupted its human bearer!
Directed by: Peter Foxen
In this example:
- We define the
RingBearer
andOneRing
components to represent the bearers of the Rings and the power of the One Ring respectively. - We refer to a singleton instnace of
OneRing
to represent the One Ring. - We create Entities for each group of Rings (Elven, Dwarven, and Human) using the
EntitySpawner
, adding aRingBearer
component and a Link to the One Ring for each. - We create a Query to find all
RingBearer
Entities that are linked to ourOneRing
instance (the One Ring). - We use the Query's
For
method to iterate over the linked Rings and corrupt their bearers, updating thecorrupted
flag in theRingBearer
record.
This example showcases the expressive power of fennecs' Object Link system. By establishing Links from the Rings to the One Ring, we are able to easily query and manipulate all Entities bound to it. The One Ring's influence is modeled directly in the ECS architecture.
The use of records for the RingBearer
component allows us to cleanly update the corruption status of each bearer in a single line of code within the For
loop.