Hostility Fix + Story Work

Fixed various bugs caused by triggering wormhole animation multiple times
Sector -> Galaxy
Fixed bug in hostility system; deserialized entities' PresencePermitted was silently throwing an exception (due to Faction being null during initialization) and thus never updating in response to security level changes
Worked a bit on story processing
This commit is contained in:
Reinier Vens 2021-05-14 03:03:28 +01:00
commit 2f98904614
26 changed files with 301 additions and 255 deletions

View file

@ -4,10 +4,11 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using Ink;
using Ink.Runtime;
using Ink.Parsed;
using MessagePack;
using Path = System.IO.Path;
using Random = Unity.Mathematics.Random;
using Story = Ink.Runtime.Story;
public class StoryProcessor : IZoneResolver, IFactionResolver
{
@ -24,62 +25,67 @@ public class StoryProcessor : IZoneResolver, IFactionResolver
public string LoadInkFileContents (string fullFilename) => File.ReadAllText (fullFilename);
}
private Dictionary<Story, SectorZone> _placedStories = new Dictionary<Story, SectorZone>();
private Dictionary<Story, string[]> _storyKnotPaths = new Dictionary<Story, string[]>();
private Dictionary<Story, GalaxyZone> _storyLocations = new Dictionary<Story, GalaxyZone>();
private Dictionary<string, Story> _processedStories = new Dictionary<string, Story>();
private DirectoryInfo _locationsPath;
private DirectoryInfo _questsPath;
private IFileHandler _inkFileHandler;
public DirectoryInfo NarrativeDirectory { get; }
private Random _random;
public Sector Sector { get; }
public Galaxy Galaxy { get; }
public PlayerSettings Settings { get; }
public StoryProcessor(PlayerSettings settings, DirectoryInfo narrativeDirectory, Sector sector, ref Random random)
public StoryProcessor(PlayerSettings settings, DirectoryInfo narrativeDirectory, Galaxy galaxy, ref Random random)
{
NarrativeDirectory = narrativeDirectory;
Sector = sector;
Galaxy = galaxy;
_random = random;
Settings = settings;
_inkFileHandler = new AetheriaInkFileHandler(narrativeDirectory);
_locationsPath = narrativeDirectory.CreateSubdirectory("Locations");
_questsPath = narrativeDirectory.CreateSubdirectory("Quests");
}
public Dictionary<Story, SectorZone> PlaceStories()
public void ProcessStories()
{
var locationFiles = _locationsPath.EnumerateFiles("*.ink");
foreach(var inkFile in locationFiles) PlaceStory(GetStory(inkFile));
return _placedStories;
foreach(var inkFile in locationFiles) ProcessLocation(GetStory(inkFile), inkFile);
var questFiles = _questsPath.EnumerateFiles("*ink");
foreach(var inkFile in questFiles) ProcessQuest(GetStory(inkFile));
}
public SectorZone ResolveZone(string path)
public GalaxyZone ResolveZone(string path)
{
// Handle special cases: "start" and "end"
if (path.Equals("start", StringComparison.InvariantCultureIgnoreCase))
return Sector.Entrance;
return Galaxy.Entrance;
if (path.Equals("end", StringComparison.InvariantCultureIgnoreCase))
return Sector.Exit;
return Galaxy.Exit;
// Handle faction home reference: format = "home.<faction>"
if(path.StartsWith("home"))
{
var factionString = path.Substring(5);
var faction = ResolveFaction(factionString);
return !Sector.HomeZones.ContainsKey(faction) ? null : Sector.HomeZones[faction];
return !Galaxy.HomeZones.ContainsKey(faction) ? null : Galaxy.HomeZones[faction];
}
var locationInkFile = new FileInfo(Path.Combine(_locationsPath.FullName, $"{path}.ink"));
if (!locationInkFile.Exists) return null;
var story = GetStory(locationInkFile);
PlaceStory(story);
return _placedStories.ContainsKey(story) ? _placedStories[story] : null;
ProcessLocation(story, locationInkFile);
return _storyLocations.ContainsKey(story) ? _storyLocations[story] : null;
}
public Faction ResolveFaction(string name)
{
return Sector.Factions.FirstOrDefault(f => f.Name.StartsWith(name, StringComparison.InvariantCultureIgnoreCase));
return Galaxy.Factions.FirstOrDefault(f => f.Name.StartsWith(name, StringComparison.InvariantCultureIgnoreCase));
}
Story GetStory(FileInfo inkFile)
@ -109,16 +115,30 @@ public class StoryProcessor : IZoneResolver, IFactionResolver
fileHandler = _inkFileHandler
});
var story = compiler.Compile();
var knots = compiler.parsedStory.FindAll<Knot>().Select(k=>k.runtimePath.ToString()).ToArray();
_storyKnotPaths[story] = knots;
File.WriteAllText(compiledFileName, story.ToJson());
return story;
}
public void PlaceStory(Story story)
{
if (_placedStories.ContainsKey(story)) return; // Don't place already placed stories, idiot!
_placedStories[story] = null; // Avoids potential for infinite loops when evaluating constraints
var contentTags = GetContentTags(story);
public void ProcessQuest(Story story)
{
var quest = new GalaxyQuest();
quest.Story = story;
foreach (var knot in _storyKnotPaths[story])
{
var tags = GetContentTags(story.TagsForContentAtPath(knot));
}
}
public void ProcessLocation(Story story, FileInfo inkFile)
{
if (_storyLocations.ContainsKey(story)) return; // Don't place already placed stories, idiot!
_storyLocations[story] = null; // Avoids potential for infinite loops when evaluating constraints
var fileName = Path.GetFileNameWithoutExtension(inkFile.FullName);
var contentTags = GetContentTags(story.globalTags);
var constraints = new List<ZoneConstraint>();
if(contentTags.ContainsKey("constraint"))
@ -157,16 +177,34 @@ public class StoryProcessor : IZoneResolver, IFactionResolver
else
selector = new RandomSelector(ref _random);
var zoneCandidates = new List<SectorZone>();
zoneCandidates.AddRange(Sector.Zones.Where(z=>constraints.All(c=>c.Test(z))));
_placedStories[story] = selector.SelectZone(zoneCandidates);
var zoneCandidates = new List<GalaxyZone>();
zoneCandidates.AddRange(Galaxy.Zones.Where(z=>!z.NamedZone && constraints.All(c=>c.Test(z))));
var zone = selector.SelectZone(zoneCandidates);
var location = new LocationStory
{
FileName = fileName,
Name = contentTags.ContainsKey("name") ? contentTags["name"].First() : fileName,
Faction = contentTags.ContainsKey("faction") ? ResolveFaction(contentTags["faction"].First()) : zone.Owner,
Security = contentTags.ContainsKey("security")
? (SecurityLevel) Enum.Parse(typeof(SecurityLevel), contentTags["security"].First(), true)
: SecurityLevel.Open,
Story = story,
Type = contentTags.ContainsKey("type")
? (LocationType) Enum.Parse(typeof(LocationType), contentTags["type"].First(), true)
: LocationType.Station
};
zone.Locations.Add(location);
_storyLocations[story] = zone;
}
// Parse all tags of the format X:Y into a dictionary with a list of every Y for each X
public static Dictionary<string, List<string>> GetContentTags(Story story)
public static Dictionary<string, List<string>> GetContentTags(List<string> tags)
{
var contentTags = new Dictionary<string, List<string>>();
foreach (var tag in story.globalTags)
foreach (var tag in tags)
{
if (tag.IndexOf(':') == -1) continue;

View file

@ -4,7 +4,7 @@ using System.Linq;
public interface IZoneResolver
{
public SectorZone ResolveZone(string path);
public GalaxyZone ResolveZone(string path);
}
public interface IFactionResolver
@ -16,12 +16,12 @@ public abstract class ZoneConstraint
{
public bool Flip = false;
public bool Test(SectorZone zone)
public bool Test(GalaxyZone zone)
{
return Flip ^ TestZone(zone);
}
protected abstract bool TestZone(SectorZone zone);
protected abstract bool TestZone(GalaxyZone zone);
}
public class FactionPresenceConstraint : ZoneConstraint
@ -32,7 +32,7 @@ public class FactionPresenceConstraint : ZoneConstraint
TargetFaction = resolver.ResolveFaction(args[0]);
}
protected override bool TestZone(SectorZone zone)
protected override bool TestZone(GalaxyZone zone)
{
return zone.Factions.Any(f=>f.ID == TargetFaction?.ID);
}
@ -47,7 +47,7 @@ public class FactionOwnerConstraint : ZoneConstraint
TargetFaction = resolver.ResolveFaction(args[0]);
}
protected override bool TestZone(SectorZone zone)
protected override bool TestZone(GalaxyZone zone)
{
return zone.Owner.ID == TargetFaction?.ID;
}
@ -55,7 +55,7 @@ public class FactionOwnerConstraint : ZoneConstraint
public class DistanceConstraint : ZoneConstraint
{
private SectorZone _targetZone;
private GalaxyZone _targetZone;
private Predicate<int> _test;
public DistanceConstraint(string[] args, IZoneResolver zoneResolver)
@ -82,7 +82,7 @@ public class DistanceConstraint : ZoneConstraint
}
}
protected override bool TestZone(SectorZone zone)
protected override bool TestZone(GalaxyZone zone)
{
return _targetZone != null && _test(zone.Distance[_targetZone]);
}

View file

@ -6,16 +6,16 @@ using Random = Unity.Mathematics.Random;
public abstract class ZoneSelector
{
public abstract SectorZone SelectZone(List<SectorZone> candidates);
public abstract GalaxyZone SelectZone(List<GalaxyZone> candidates);
}
public abstract class OrderedZoneSelector : ZoneSelector
{
public bool Flip = false;
protected abstract Func<SectorZone, IComparable> Comparison { get; }
protected abstract Func<GalaxyZone, IComparable> Comparison { get; }
public override SectorZone SelectZone(List<SectorZone> candidates)
public override GalaxyZone SelectZone(List<GalaxyZone> candidates)
{
var sorted = candidates.OrderBy(Comparison);
return Flip ? sorted.Last() : sorted.First();
@ -24,7 +24,7 @@ public abstract class OrderedZoneSelector : ZoneSelector
public class DistanceSelector : OrderedZoneSelector
{
private SectorZone Target { get; }
private GalaxyZone Target { get; }
public DistanceSelector(string[] args, IZoneResolver zoneResolver)
@ -32,7 +32,7 @@ public class DistanceSelector : OrderedZoneSelector
Target = zoneResolver.ResolveZone(args[0].Trim());
}
protected override Func<SectorZone, IComparable> Comparison => zone => Target?.Distance[zone] ?? 0;
protected override Func<GalaxyZone, IComparable> Comparison => zone => Target?.Distance[zone] ?? 0;
}
public class RandomSelector : ZoneSelector
@ -44,7 +44,7 @@ public class RandomSelector : ZoneSelector
Random = random;
}
public override SectorZone SelectZone(List<SectorZone> candidates)
public override GalaxyZone SelectZone(List<GalaxyZone> candidates)
{
return candidates[Random.NextInt(candidates.Count)];
}