From b0673c82fc5448f669ecc5827d3f38f960a69111 Mon Sep 17 00:00:00 2001 From: Reinier Vens Date: Sun, 16 May 2021 05:24:51 +0100 Subject: [PATCH] Multi-Location Quest Injection --- Assets/Materials/UI/Map Gravity Lines.mat | 2 +- Assets/Prefabs/UI/Choice.prefab | 4 +- Assets/Scenes/ARPG.unity | 6 +- Assets/Scripts/Gameplay/ActionGameManager.cs | 3 +- Assets/Scripts/ServerShared/Entity.cs | 6 +- .../Scripts/ServerShared/EntitySerializer.cs | 3 +- Assets/Scripts/ServerShared/Enums.cs | 1 + Assets/Scripts/ServerShared/Galaxy.cs | 23 ++-- .../ServerShared/Narrative/StoryProcessor.cs | 92 +++++++++----- Assets/Scripts/ServerShared/OrbitalEntity.cs | 1 + Assets/Scripts/ServerShared/ZoneGenerator.cs | 114 ++++++++++++------ Assets/Scripts/UI/MainMenu.cs | 5 +- Assets/Scripts/UI/Menu/LocalMenu.cs | 80 +++++++++--- Assets/Scripts/UI/Menu/MenuPanel.cs | 1 + GameData/Narrative/Locations/TestA.ink | 31 +++++ GameData/Narrative/Locations/TestA.json | 1 + GameData/Narrative/Quests/TestQuest.ink | 12 ++ GameData/Narrative/Quests/TestQuest.json | 1 + .../{Locations => }/RSA/QuestTexts.ink | 0 .../RSA/TheActualFactualFactory.ink | 0 .../{Locations => }/ReconStationAlpha.ink | 0 GameData/PlayerSettings.msgpack | 4 +- RethinkDB Queries.txt | 4 +- 23 files changed, 284 insertions(+), 110 deletions(-) create mode 100644 GameData/Narrative/Locations/TestA.ink create mode 100644 GameData/Narrative/Locations/TestA.json create mode 100644 GameData/Narrative/Quests/TestQuest.ink create mode 100644 GameData/Narrative/Quests/TestQuest.json rename GameData/Narrative/{Locations => }/RSA/QuestTexts.ink (100%) rename GameData/Narrative/{Locations => }/RSA/TheActualFactualFactory.ink (100%) rename GameData/Narrative/{Locations => }/ReconStationAlpha.ink (100%) diff --git a/Assets/Materials/UI/Map Gravity Lines.mat b/Assets/Materials/UI/Map Gravity Lines.mat index 762784dcd..749d876be 100644 --- a/Assets/Materials/UI/Map Gravity Lines.mat +++ b/Assets/Materials/UI/Map Gravity Lines.mat @@ -86,7 +86,7 @@ Material: - _Mode: 0 - _OcclusionStrength: 1 - _Parallax: 0.02 - - _Scale: 1.6706811 + - _Scale: 0.5 - _SmoothnessTextureChannel: 0 - _SpecularHighlights: 1 - _SrcBlend: 1 diff --git a/Assets/Prefabs/UI/Choice.prefab b/Assets/Prefabs/UI/Choice.prefab index 212919031..7aff99027 100644 --- a/Assets/Prefabs/UI/Choice.prefab +++ b/Assets/Prefabs/UI/Choice.prefab @@ -124,7 +124,7 @@ MonoBehaviour: m_fontColor32: serializedVersion: 2 rgba: 4294967295 - m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_fontColor: {r: 0.6981132, g: 0.6981132, b: 0.6981132, a: 1} m_enableVertexGradient: 0 m_colorMode: 3 m_fontColorGradient: @@ -148,7 +148,7 @@ MonoBehaviour: m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 - m_HorizontalAlignment: 1 + m_HorizontalAlignment: 2 m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 0 diff --git a/Assets/Scenes/ARPG.unity b/Assets/Scenes/ARPG.unity index 45a62ffe4..65c8c3956 100644 --- a/Assets/Scenes/ARPG.unity +++ b/Assets/Scenes/ARPG.unity @@ -22583,7 +22583,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &1485884352 RectTransform: m_ObjectHideFlags: 0 @@ -23721,7 +23721,7 @@ PrefabInstance: - target: {fileID: 878382121558056004, guid: 2758dfa2744d7ad4d8877545189d116e, type: 3} propertyPath: m_AnchoredPosition.x - value: 0 + value: 55.675 objectReference: {fileID: 0} - target: {fileID: 878382121558056004, guid: 2758dfa2744d7ad4d8877545189d116e, type: 3} @@ -24933,7 +24933,7 @@ PrefabInstance: - target: {fileID: 878382121558056004, guid: 2758dfa2744d7ad4d8877545189d116e, type: 3} propertyPath: m_AnchoredPosition.x - value: 0 + value: 60.98 objectReference: {fileID: 0} - target: {fileID: 878382121558056004, guid: 2758dfa2744d7ad4d8877545189d116e, type: 3} diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index 12269baba..9cc91decc 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -234,7 +234,6 @@ public class ActionGameManager : MonoBehaviour public ItemManager ItemManager { get; private set; } public Zone Zone { get; private set; } public List Loadouts { get; } = new List(); - public IEnumerable GetStories => _stories; private readonly (float2 direction, string name)[] _directions = { (float2(0, 1), "Front"), @@ -488,7 +487,7 @@ public class ActionGameManager : MonoBehaviour return slot; } - foreach (var actionBarInput in PlayerSettings.InputSettings.ActionBarInputs) createBinding(actionBarInput); + foreach (var actionBarInput in PlayerSettings.InputSettings.ActionBarInputs.OrderBy(i=>i)) createBinding(actionBarInput); #endregion diff --git a/Assets/Scripts/ServerShared/Entity.cs b/Assets/Scripts/ServerShared/Entity.cs index f5692ca10..97871f50e 100644 --- a/Assets/Scripts/ServerShared/Entity.cs +++ b/Assets/Scripts/ServerShared/Entity.cs @@ -42,8 +42,8 @@ public abstract class Entity public string Name; - public int Population; - public Dictionary Personality = new Dictionary(); + // public int Population; + // public Dictionary Personality = new Dictionary(); public readonly Dictionary Messages = new Dictionary(); public readonly Dictionary VisibilitySources = new Dictionary(); @@ -288,7 +288,7 @@ public abstract class Entity // TODO: Inter-faction hostility // When the entity faction owns the zone, they are hostile to trespassers or those hostile to them - if (Faction.ID == Zone.GalaxyZone.Owner.ID) + if (Faction.ID == Zone.GalaxyZone.Owner?.ID) return recursive ? !(other.PresencePermitted?.Value ?? true) : !(other.PresencePermitted?.Value ?? true)|| other.IsHostileTo(this, true); return !recursive && other.IsHostileTo(this, true); diff --git a/Assets/Scripts/ServerShared/EntitySerializer.cs b/Assets/Scripts/ServerShared/EntitySerializer.cs index ba78698ca..0a4f0e77b 100644 --- a/Assets/Scripts/ServerShared/EntitySerializer.cs +++ b/Assets/Scripts/ServerShared/EntitySerializer.cs @@ -85,6 +85,7 @@ public static class EntitySerializer Restore(itemManager, zone, pack, entity, instantiate); entity.SecurityLevel = pack.SecurityLevel; entity.SecurityRadius = pack.SecurityRadius; + if (pack.Story >= 0) entity.Story = zone.GalaxyZone.Locations[pack.Story]; return entity; } @@ -166,7 +167,7 @@ public class ShipPack : EntityPack public class OrbitalEntityPack : EntityPack { [Key(17)] public Guid Orbit; - [Key(18)] public SavedStory Story; + [Key(18)] public int Story = -1; [Key(19)] public SecurityLevel SecurityLevel; [Key(20)] public float SecurityRadius; } diff --git a/Assets/Scripts/ServerShared/Enums.cs b/Assets/Scripts/ServerShared/Enums.cs index 8eb6c7f4d..df60aea23 100644 --- a/Assets/Scripts/ServerShared/Enums.cs +++ b/Assets/Scripts/ServerShared/Enums.cs @@ -168,6 +168,7 @@ public enum CompoundCommodityCategory public enum LocationType { Station, + Asteroid, Planet } diff --git a/Assets/Scripts/ServerShared/Galaxy.cs b/Assets/Scripts/ServerShared/Galaxy.cs index 588c7af20..2686b4009 100644 --- a/Assets/Scripts/ServerShared/Galaxy.cs +++ b/Assets/Scripts/ServerShared/Galaxy.cs @@ -26,6 +26,7 @@ public class Galaxy public GalaxyZone Entrance { get; } public GalaxyZone Exit { get; } public Dictionary FactionRelationships { get; } = new Dictionary(); + private Action Log { get; } private HashSet _containedFactions; private GalaxyZone[] _exitPath; @@ -41,8 +42,9 @@ public class Galaxy } } - public Galaxy(CultCache cultCache, SavedGame savedGame) + public Galaxy(CultCache cultCache, SavedGame savedGame, Action log) { + Log = log; Background = savedGame.Background; Factions = savedGame.Factions.Select(cultCache.Get).ToArray(); @@ -87,11 +89,13 @@ public class Galaxy SectorGenerationSettings settings, SectorBackgroundSettings background, NameGeneratorSettings nameGeneratorSettings, - CultCache cache, + CultCache cache, + Action log, Action progressCallback = null, uint seed = 0) { Background = background; + Log = log; var factions = cache.GetAll(); var random = new Random(seed == 0 ? (uint) (DateTime.Now.Ticks % uint.MaxValue) : seed); Factions = factions.OrderBy(x => random.NextFloat()).Take(settings.MegaCount).ToArray(); @@ -129,12 +133,14 @@ public class Galaxy CultCache cache, PlayerSettings playerSettings, DirectoryInfo narrativeDirectory, + Action log, Action progressCallback = null, uint seed = 0) { Faction ResolveFaction(string name) => cache.GetAll().FirstOrDefault(f => f.Name.StartsWith(name, StringComparison.InvariantCultureIgnoreCase)); Background = background; + Log = log; var random = new Random(seed == 0 ? (uint) (DateTime.Now.Ticks % uint.MaxValue) : seed); var factions = new List(); @@ -219,12 +225,8 @@ public class Galaxy GenerateNames(cache, nameGeneratorSettings, ref random, progressCallback); progressCallback?.Invoke("Weaving Narrative"); - // var processor = new StoryProcessor(playerSettings, narrativeDirectory, this, ref random); - // var storyPlacement = processor.PlaceStories(); - // foreach (var story in storyPlacement.Keys) - // { - // storyPlacement[story] - // } + var processor = new StoryProcessor(playerSettings, narrativeDirectory, this, ref random, Log); + processor.ProcessStories(); progressCallback?.Invoke("Done!"); if(progressCallback!=null) Thread.Sleep(500); // Inserting Delay to make it seem like it's doing more work lmao @@ -554,15 +556,18 @@ public class GalaxyZone public class GalaxyQuest { public Story Story; - public Dictionary Locations = new Dictionary(); + public Dictionary KnotLocations = new Dictionary(); } public class LocationStory { + public GalaxyZone Zone; public string FileName; public string Name; public Story Story; public Faction Faction; public SecurityLevel Security; public LocationType Type; + public int Turrets; + public Dictionary> KnotQuests = new Dictionary>(); } \ No newline at end of file diff --git a/Assets/Scripts/ServerShared/Narrative/StoryProcessor.cs b/Assets/Scripts/ServerShared/Narrative/StoryProcessor.cs index af240037a..d253781d8 100644 --- a/Assets/Scripts/ServerShared/Narrative/StoryProcessor.cs +++ b/Assets/Scripts/ServerShared/Narrative/StoryProcessor.cs @@ -26,22 +26,24 @@ public class StoryProcessor : IZoneResolver, IFactionResolver } private Dictionary _storyKnotPaths = new Dictionary(); - private Dictionary _storyLocations = new Dictionary(); + private Dictionary _placedStories = new Dictionary(); private Dictionary _processedStories = new Dictionary(); private DirectoryInfo _locationsPath; private DirectoryInfo _questsPath; private IFileHandler _inkFileHandler; - public DirectoryInfo NarrativeDirectory { get; } private Random _random; public Galaxy Galaxy { get; } public PlayerSettings Settings { get; } + + private Action Log { get; } - public StoryProcessor(PlayerSettings settings, DirectoryInfo narrativeDirectory, Galaxy galaxy, ref Random random) + public StoryProcessor(PlayerSettings settings, DirectoryInfo narrativeDirectory, Galaxy galaxy, ref Random random, Action log) { NarrativeDirectory = narrativeDirectory; Galaxy = galaxy; _random = random; + Log = log; Settings = settings; _inkFileHandler = new AetheriaInkFileHandler(narrativeDirectory); @@ -56,7 +58,7 @@ public class StoryProcessor : IZoneResolver, IFactionResolver foreach(var inkFile in locationFiles) ProcessLocation(GetStory(inkFile), inkFile); var questFiles = _questsPath.EnumerateFiles("*ink"); - foreach(var inkFile in questFiles) ProcessQuest(GetStory(inkFile)); + foreach(var inkFile in questFiles) ProcessQuest(GetStory(inkFile), inkFile); } public GalaxyZone ResolveZone(string path) @@ -74,13 +76,18 @@ public class StoryProcessor : IZoneResolver, IFactionResolver var faction = ResolveFaction(factionString); return !Galaxy.HomeZones.ContainsKey(faction) ? null : Galaxy.HomeZones[faction]; } - + + return ResolveLocation(path)?.Zone; + } + + public LocationStory ResolveLocation(string path) + { var locationInkFile = new FileInfo(Path.Combine(_locationsPath.FullName, $"{path}.ink")); if (!locationInkFile.Exists) return null; var story = GetStory(locationInkFile); ProcessLocation(story, locationInkFile); - return _storyLocations.ContainsKey(story) ? _storyLocations[story] : null; + return _placedStories.ContainsKey(story) ? _placedStories[story] : null; } public Faction ResolveFaction(string name) @@ -95,20 +102,20 @@ public class StoryProcessor : IZoneResolver, IFactionResolver // If the story has already been read / compiled, return it directly if (_processedStories.ContainsKey(fileName)) return _processedStories[fileName]; - var compiledFileName = Path.Combine(inkFile.Directory.FullName, $"{fileName}.json"); - - // Calculate a hash of the ink file - var hash = File.ReadAllBytes(inkFile.FullName).GetHashSHA1(); - - if (File.Exists(compiledFileName) && // If a compiled version of the ink file exists - Settings.HashedStoryFiles.ContainsKey(fileName) && // And its hash has been saved - hash == Settings.HashedStoryFiles[fileName]) // And the saved hash matches the current hash - { - // Load the story from the existing compiled JSON representation - return new Story(File.ReadAllText(compiledFileName)); - } - - Settings.HashedStoryFiles[fileName] = hash; + // var compiledFileName = Path.Combine(inkFile.Directory.FullName, $"{fileName}.json"); + // + // // Calculate a hash of the ink file + // var hash = File.ReadAllBytes(inkFile.FullName).GetHashSHA1(); + // + // if (File.Exists(compiledFileName) && // If a compiled version of the ink file exists + // Settings.HashedStoryFiles.ContainsKey(fileName) && // And its hash has been saved + // hash == Settings.HashedStoryFiles[fileName]) // And the saved hash matches the current hash + // { + // // Load the story from the existing compiled JSON representation + // return new Story(File.ReadAllText(compiledFileName)); + // } + // + // Settings.HashedStoryFiles[fileName] = hash; var compiler = new Compiler(File.ReadAllText(inkFile.FullName), new Compiler.Options { countAllVisits = true, @@ -117,25 +124,45 @@ public class StoryProcessor : IZoneResolver, IFactionResolver var story = compiler.Compile(); var knots = compiler.parsedStory.FindAll().Select(k=>k.runtimePath.ToString()).ToArray(); _storyKnotPaths[story] = knots; - File.WriteAllText(compiledFileName, story.ToJson()); + //File.WriteAllText(compiledFileName, story.ToJson()); + _processedStories[fileName] = story; return story; } - public void ProcessQuest(Story story) + public void ProcessQuest(Story story, FileInfo inkFile) { + var fileName = Path.GetFileNameWithoutExtension(inkFile.FullName); var quest = new GalaxyQuest(); quest.Story = story; foreach (var knot in _storyKnotPaths[story]) { - var tags = GetContentTags(story.TagsForContentAtPath(knot)); - + var tags = story.TagsForContentAtPath(knot); + if(tags==null) continue; + var contentTags = GetContentTags(tags); + if (contentTags.ContainsKey("location")) + { + var locationName = contentTags["location"].First(); + var locationStory = ResolveLocation(locationName); + if (tags.Contains("required") && locationStory == null) + { + Log($"Quest \"{fileName}\" unable to find required location \"{locationName}\" for knot \"{knot}\""); + return; + } + quest.KnotLocations[knot] = locationStory; + } + } + + foreach (var x in quest.KnotLocations) + { + if (!x.Value.KnotQuests.ContainsKey(x.Key)) x.Value.KnotQuests[x.Key] = new List(); + x.Value.KnotQuests[x.Key].Add(quest); } } 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 + if (_placedStories.ContainsKey(story)) return; // Don't place already placed stories, idiot! + _placedStories[story] = null; // Avoids potential for infinite loops when evaluating constraints var fileName = Path.GetFileNameWithoutExtension(inkFile.FullName); var contentTags = GetContentTags(story.globalTags); @@ -184,6 +211,7 @@ public class StoryProcessor : IZoneResolver, IFactionResolver var location = new LocationStory { + Zone = zone, FileName = fileName, Name = contentTags.ContainsKey("name") ? contentTags["name"].First() : fileName, Faction = contentTags.ContainsKey("faction") ? ResolveFaction(contentTags["faction"].First()) : zone.Owner, @@ -193,11 +221,17 @@ public class StoryProcessor : IZoneResolver, IFactionResolver Story = story, Type = contentTags.ContainsKey("type") ? (LocationType) Enum.Parse(typeof(LocationType), contentTags["type"].First(), true) - : LocationType.Station + : LocationType.Station, + Turrets = contentTags.ContainsKey("turrets") ? int.Parse(contentTags["turrets"].First()) : 0 }; zone.Locations.Add(location); - _storyLocations[story] = zone; + if (contentTags.ContainsKey("namezone")) + { + zone.Name = contentTags["namezone"].First(); + } + + _placedStories[story] = location; } // Parse all tags of the format X:Y into a dictionary with a list of every Y for each X @@ -209,7 +243,7 @@ public class StoryProcessor : IZoneResolver, IFactionResolver if (tag.IndexOf(':') == -1) continue; var tokens = tag.Split(':'); - var leftSide = tokens[0].Trim(); + var leftSide = tokens[0].Trim().ToLowerInvariant(); var rightSide = tokens[1].Trim(); if (!contentTags.ContainsKey(leftSide)) contentTags[leftSide] = new List(); contentTags[leftSide].Add(rightSide); diff --git a/Assets/Scripts/ServerShared/OrbitalEntity.cs b/Assets/Scripts/ServerShared/OrbitalEntity.cs index 30325b64c..ea4f09b59 100644 --- a/Assets/Scripts/ServerShared/OrbitalEntity.cs +++ b/Assets/Scripts/ServerShared/OrbitalEntity.cs @@ -14,6 +14,7 @@ public class OrbitalEntity : Entity public Guid OrbitData; public SecurityLevel SecurityLevel; public float SecurityRadius; + public LocationStory Story; public OrbitalEntity(ItemManager itemManager, Zone zone, EquippableItem hull, Guid orbit, EntitySettings settings) : base(itemManager, zone, hull, settings) { diff --git a/Assets/Scripts/ServerShared/ZoneGenerator.cs b/Assets/Scripts/ServerShared/ZoneGenerator.cs index ddd65cb58..ea09f36dc 100644 --- a/Assets/Scripts/ServerShared/ZoneGenerator.cs +++ b/Assets/Scripts/ServerShared/ZoneGenerator.cs @@ -226,7 +226,8 @@ public static class ZoneGenerator var nearestFactionHomeZone = galaxy.HomeZones[nearestFaction]; var factionPresence = nearestFaction.InfluenceDistance - nearestFactionHomeZone.Distance[galaxyZone] + 1; - var stationCount = (int)(random.NextFloat() * (factionPresence + 1)); + var storyStations = galaxyZone.Locations.Where(story => story.Type == LocationType.Station).ToArray(); + var stationCount = (int)(random.NextFloat() * (factionPresence + 1)) + storyStations.Length; var potentialLagrangePoints = planets .Where(p => p.Parent != null && p.Parent.Children .TrueForAll(c => !(c != p && abs(c.Distance - p.Distance) < .1f))) // Filter Rosettes @@ -238,59 +239,92 @@ public static class ZoneGenerator .Take(stationCount) .Select(p=>orbitMap[p]) .ToArray(); - - var loadoutGenerator = isTutorial ? new LoadoutGenerator(ref random, itemManager, nearestFaction, .5f) : - new LoadoutGenerator(ref random, itemManager, galaxy, galaxyZone, nearestFaction, .5f); - - foreach (var orbit in selectedStationOrbits) + + var loadoutGenerators = new Dictionary(); + + LoadoutGenerator GetLoadoutGenerator(Faction faction) + { + if (!loadoutGenerators.ContainsKey(faction)) + loadoutGenerators[faction] = isTutorial + ? new LoadoutGenerator(ref random, itemManager, faction, .5f) + : new LoadoutGenerator(ref random, itemManager, galaxy, galaxyZone, faction, .5f); + return loadoutGenerators[faction]; + } + + OrbitData CreateLagrangeOrbit(OrbitData baseOrbit) { - var security = (SecurityLevel) ((int) ((1f - pow(random.NextFloat(), factionPresence / 2f)) * 3f)); var lagrangeOrbit = new OrbitData { ID = Guid.NewGuid(), - Parent = orbit.Parent, - Distance = new ReactiveProperty(orbit.Distance.Value), - Phase = orbit.Phase + PI / 3 * sign(random.NextFloat() - .5f) + Parent = baseOrbit.Parent, + Distance = new ReactiveProperty(baseOrbit.Distance.Value), + Phase = baseOrbit.Phase + PI / 3 * sign(random.NextFloat() - .5f) }; pack.Orbits.Add(lagrangeOrbit); + return lagrangeOrbit; + } + + void PlaceTurret(OrbitData orbit, LoadoutGenerator loadoutGenerator, int distanceMultiplier) + { + var phase = 20f * distanceMultiplier / orbit.Distance.Value; - var station = loadoutGenerator.GenerateStationLoadout(); + var turretOrbit = new OrbitData + { + ID = Guid.NewGuid(), + Parent = orbit.Parent, + Distance = new ReactiveProperty(orbit.Distance.Value), + Phase = orbit.Phase + phase + }; + pack.Orbits.Add(turretOrbit); + var turret = loadoutGenerator.GenerateTurretLoadout(); + turret.Orbit = turretOrbit.ID; + pack.Entities.Add(turret); + } + + void PlaceTurrets(OrbitData orbit, LoadoutGenerator loadoutGenerator, int count) + { + for(int t=0; t(orbit.Distance.Value), - Phase = lagrangeOrbit.Phase + turretPhase - }; - pack.Orbits.Add(leadingTurretOrbit); - var leadingTurret = loadoutGenerator.GenerateTurretLoadout(); - leadingTurret.Orbit = leadingTurretOrbit.ID; - pack.Entities.Add(leadingTurret); - - var trailingTurretOrbit = new OrbitData - { - ID = Guid.NewGuid(), - Parent = orbit.Parent, - Distance = new ReactiveProperty(orbit.Distance.Value), - Phase = lagrangeOrbit.Phase - turretPhase - }; - pack.Orbits.Add(trailingTurretOrbit); - var trailingTurret = loadoutGenerator.GenerateTurretLoadout(); - trailingTurret.Orbit = trailingTurretOrbit.ID; - pack.Entities.Add(trailingTurret); + + PlaceTurrets(lagrangeOrbit, GetLoadoutGenerator(nearestFaction), 2); } - + var enemyCount = (int)(random.NextFloat() * factionPresence * 2) + stationCount; for (int i = 0; i < enemyCount; i++) { - pack.Entities.Add(loadoutGenerator.GenerateShipLoadout()); + pack.Entities.Add(GetLoadoutGenerator(nearestFaction).GenerateShipLoadout()); } return pack; diff --git a/Assets/Scripts/UI/MainMenu.cs b/Assets/Scripts/UI/MainMenu.cs index a91622967..c835f4ecb 100644 --- a/Assets/Scripts/UI/MainMenu.cs +++ b/Assets/Scripts/UI/MainMenu.cs @@ -100,7 +100,8 @@ public class MainMenu : MonoBehaviour ActionGameManager.IsTutorial = ActionGameManager.PlayerSettings.SavedRun.IsTutorial; ActionGameManager.CurrentGalaxy = new Galaxy( ActionGameManager.CultCache, - ActionGameManager.PlayerSettings.SavedRun); + ActionGameManager.PlayerSettings.SavedRun, + Debug.Log); SceneManager.LoadScene("ARPG"); }); else @@ -130,6 +131,7 @@ public class MainMenu : MonoBehaviour Settings.SectorBackgroundSettings, Settings.NameGeneratorSettings, ActionGameManager.CultCache, + Debug.Log, setState); Observable.NextFrame().Subscribe(_ => { @@ -158,6 +160,7 @@ public class MainMenu : MonoBehaviour ActionGameManager.CultCache, ActionGameManager.PlayerSettings, ActionGameManager.GameDataDirectory.CreateSubdirectory("Narrative"), + Debug.Log, setState); Observable.NextFrame().Subscribe(_ => { diff --git a/Assets/Scripts/UI/Menu/LocalMenu.cs b/Assets/Scripts/UI/Menu/LocalMenu.cs index 88259f0dd..996ec3863 100644 --- a/Assets/Scripts/UI/Menu/LocalMenu.cs +++ b/Assets/Scripts/UI/Menu/LocalMenu.cs @@ -14,13 +14,20 @@ public class LocalMenu : MonoBehaviour public TextMeshProUGUI Output; public RectTransform ChoiceParent; public ChoicePrefab ChoicePrefab; - - private Story _currentStory; + + private string _currentPath; + private LocationStory _currentLocation; + private Story _activeStory; private List _choiceInstances = new List(); private void OnEnable() { - _currentStory = ActionGameManager.Instance.GetStories.First(); + if (ActionGameManager.Instance.DockedEntity is OrbitalEntity orbital) + { + _currentLocation = orbital.Story; + _activeStory = _currentLocation.Story; + } + else return; Continue(); } @@ -28,31 +35,72 @@ public class LocalMenu : MonoBehaviour { ContinueTrigger.OnPointerClickAsObservable().Subscribe(pointerEvent => { - if (_currentStory == null) return; - - if(_currentStory.canContinue) Continue(); + if (_activeStory == null) return; + Continue(); }); } void Continue() { - Output.text = _currentStory.Continue(); + if (!_activeStory.state.previousPointer.isNull) _currentPath = _activeStory.state.previousPointer.path.head.name; + if(_activeStory.canContinue) _activeStory.Continue(); + if (!_activeStory.state.previousPointer.isNull) _currentPath = _activeStory.state.previousPointer.path.head.name; + Output.text = _activeStory.currentText; foreach(var instance in _choiceInstances) Destroy(instance); _choiceInstances.Clear(); - foreach (var choice in _currentStory.currentChoices) + if(_activeStory.currentChoices.Any()) { - var choiceInstance = Instantiate(ChoicePrefab, ChoiceParent); - choiceInstance.Label.text = choice.text; - choiceInstance.Button.onClick.AddListener(() => - { - _currentStory.ChoosePath(choice.targetPath); - Continue(); - }); - _choiceInstances.Add(choiceInstance.gameObject); + PresentCurrentChoices(); } + else if (!_activeStory.canContinue) + { + // There's no choices, but we also can't continue; indicates we hit an END + if (_activeStory == _currentLocation.Story) + { + // END inside location-based story thread, restart the story + _activeStory.ResetState(); + Continue(); + } + else + { + // END inside quest content, switch back to location thread and present choices + _activeStory = _currentLocation.Story; + Continue(); + } + } + } + + void PresentCurrentChoices() + { + Debug.Log($"Current Path: \"{_currentPath}\" in {(_activeStory == _currentLocation.Story ? "Location Story" : "Quest Story")}"); + if(!string.IsNullOrEmpty(_currentPath) && _currentLocation.KnotQuests.ContainsKey(_currentPath)) + { + foreach (var quest in _currentLocation.KnotQuests[_currentPath]) + { + if (quest.Story == _activeStory) continue; // Don't repeat choices for active injected branch + + quest.Story.ChoosePathString(_currentPath); + quest.Story.ContinueMaximally(); + foreach (var choice in quest.Story.currentChoices) PresentChoice(quest.Story, choice); + } + } + foreach (var choice in _activeStory.currentChoices) PresentChoice(_activeStory, choice); + } + + void PresentChoice(Story story, Choice choice) + { + var choiceInstance = Instantiate(ChoicePrefab, ChoiceParent); + choiceInstance.Label.text = choice.text; + choiceInstance.Button.onClick.AddListener(() => + { + _activeStory = story; + _activeStory.ChoosePath(choice.targetPath); + Continue(); + }); + _choiceInstances.Add(choiceInstance.gameObject); } // Update is called once per frame diff --git a/Assets/Scripts/UI/Menu/MenuPanel.cs b/Assets/Scripts/UI/Menu/MenuPanel.cs index ce6f6ddeb..fbedf072a 100644 --- a/Assets/Scripts/UI/Menu/MenuPanel.cs +++ b/Assets/Scripts/UI/Menu/MenuPanel.cs @@ -49,6 +49,7 @@ public class MenuPanel : MonoBehaviour { tabButton.gameObject.SetActive(!tabButton.RequireDock || GameManager.DockedEntity != null); } + _tabs[MenuTab.Local].gameObject.SetActive((GameManager.DockedEntity as OrbitalEntity)?.Story!=null); } private void Awake() diff --git a/GameData/Narrative/Locations/TestA.ink b/GameData/Narrative/Locations/TestA.ink new file mode 100644 index 000000000..6585762c1 --- /dev/null +++ b/GameData/Narrative/Locations/TestA.ink @@ -0,0 +1,31 @@ +#constraint: DistanceFrom start = 0 // Place this location in the starting zone +#type: Station // Determines the type of entity for this location (Station/Asteroid/Planet) +#security: Open // Security level (Open/Secure/Critical) +#faction: Miss Terri // Locations should be tagged with the faction they belong to +#name: Test Location 1 // Will set the name of the entity as it appears in game +#nameZone: Start Zone // Will name the zone that this location is placed within - locations with named zones are excluded from being placed together +#turrets: 1 // Will place defensive turrets around the station + +-> start + +==start== +This is the start of the location story. + + + [Go to location A1] -> A1 + * This will end the story. + +- They lived happily ever after. + -> END + +==A1== +This content is in location A1. +This is the second line of content in A1. + + + [Return to start] -> start + + [Go to location A2] -> A2 + +==A2== +This content is in location A2. + + + [Return to start] -> start + * End the story ->END \ No newline at end of file diff --git a/GameData/Narrative/Locations/TestA.json b/GameData/Narrative/Locations/TestA.json new file mode 100644 index 000000000..dd822d57f --- /dev/null +++ b/GameData/Narrative/Locations/TestA.json @@ -0,0 +1 @@ +{"inkVersion":20,"root":[[{"#":"constraint: DistanceFrom start = 0"},{"#":"type: Station"},{"#":"security: Open"},{"#":"faction: Miss Terri"},{"#":"name: Test Location 1"},{"#":"nameZone: Start Zone"},{"->":"start"},["done",{"#f":5,"#n":"g-0"}],null],"done",{"start":[["^This is the start of the location story.","\n","ev","str","^Go to location A1","/str","/ev",{"*":".^.c-0","flg":4},["ev",{"^->":"start.0.8.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^This will end the story.",{"->":"$r","var":true},null]}],{"c-0":["^ ",{"->":"A1"},"\n",{"->":".^.^.g-0"},{"#f":5}],"c-1":["ev",{"^->":"start.0.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.8.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.g-0"},{"#f":5}],"g-0":["^They lived happily ever after.","\n","end",{"#f":5}]}],{"#f":1}],"A1":[["^This content is in location A1","\n","ev","str","^Return to start","/str","/ev",{"*":".^.c-0","flg":4},["ev",{"^->":"A1.0.8.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":"A1.0.c-1","flg":2},{"s":["^Choice in location A1 ",{"->":"$r","var":true},null]}],{"c-0":["^ ",{"->":"start"},"\n",{"#f":5}],"c-1":["ev",{"^->":"A1.0.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":"A1.0.8.s"},[{"#n":"$r2"}],"end","\n",{"#f":5}]}],{"#f":1}],"#f":1}],"listDefs":{}} \ No newline at end of file diff --git a/GameData/Narrative/Quests/TestQuest.ink b/GameData/Narrative/Quests/TestQuest.ink new file mode 100644 index 000000000..963bca1d9 --- /dev/null +++ b/GameData/Narrative/Quests/TestQuest.ink @@ -0,0 +1,12 @@ +==A1== +#location: TestA // Inject this knot into matching knot in location story +#required // This quest is only playable when the target location exists + + [This choice is being injected from TestQuest] -> A1A + + ==A1A== + This is a thread being injected by TestQuest + * [Proceed] -> A1B + +==A1B== +This is a child knot in an injected thread + * [Exit the injected thread] -> END \ No newline at end of file diff --git a/GameData/Narrative/Quests/TestQuest.json b/GameData/Narrative/Quests/TestQuest.json new file mode 100644 index 000000000..41095e5f9 --- /dev/null +++ b/GameData/Narrative/Quests/TestQuest.json @@ -0,0 +1 @@ +{"inkVersion":20,"root":[[["done",{"#f":5,"#n":"g-0"}],null],"done",{"A1":[[{"#":"location: TestA"},{"#":"required"},"ev","str","^This choice is being injected from TestQuest","/str","/ev",{"*":".^.c-0","flg":4},{"c-0":["^ ",{"->":"A1A"},"\n",{"#f":5}]}],{"#f":1}],"A1A":[["^This is a branch thread being injected by TestQuest","\n","ev","str","^Exit the injected thread","/str","/ev",{"*":".^.c-0","flg":20},{"c-0":["^ ","end","\n",{"#f":5}]}],{"#f":1}],"#f":1}],"listDefs":{}} \ No newline at end of file diff --git a/GameData/Narrative/Locations/RSA/QuestTexts.ink b/GameData/Narrative/RSA/QuestTexts.ink similarity index 100% rename from GameData/Narrative/Locations/RSA/QuestTexts.ink rename to GameData/Narrative/RSA/QuestTexts.ink diff --git a/GameData/Narrative/Locations/RSA/TheActualFactualFactory.ink b/GameData/Narrative/RSA/TheActualFactualFactory.ink similarity index 100% rename from GameData/Narrative/Locations/RSA/TheActualFactualFactory.ink rename to GameData/Narrative/RSA/TheActualFactualFactory.ink diff --git a/GameData/Narrative/Locations/ReconStationAlpha.ink b/GameData/Narrative/ReconStationAlpha.ink similarity index 100% rename from GameData/Narrative/Locations/ReconStationAlpha.ink rename to GameData/Narrative/ReconStationAlpha.ink diff --git a/GameData/PlayerSettings.msgpack b/GameData/PlayerSettings.msgpack index e3432f3c4..abe14649a 100644 --- a/GameData/PlayerSettings.msgpack +++ b/GameData/PlayerSettings.msgpack @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e3deabd2f0de5d092e8427fb5f87f5d0e0347d183f9e3edf327a9f3f3b65c80 -size 66911 +oid sha256:f5075c4bb409a56aebe7caff630fb60afff4420d3a48e0822326b6fe49fdf43a +size 19830 diff --git a/RethinkDB Queries.txt b/RethinkDB Queries.txt index 6e111f1fc..4e74b2e2f 100644 --- a/RethinkDB Queries.txt +++ b/RethinkDB Queries.txt @@ -28,4 +28,6 @@ r.db('Aetheria').table('Items').filter( ).update({$type:'WeaponItemData'}) // Set required enum fields in weapons -r.db('Aetheria').table('Items').filter({$type:"WeaponItemData"}).update({range:0, caliber:0, weaponType:0, fireTypes:0, modifiers:0}) \ No newline at end of file +r.db('Aetheria').table('Items').filter({$type:"WeaponItemData"}).update({range:0, caliber:0, weaponType:0, fireTypes:0, modifiers:0}) + +r.db('StreamPixels').table('Events').filter({id:"91cb76bb-41b6-4739-883b-5c6ad1fb1c78"}).update({viewers:18}) \ No newline at end of file