Skip to content

Commit

Permalink
Locations (#356)
Browse files Browse the repository at this point in the history
This is some more parts of #316. I think it covers all location
information required by dependency/milestone analysis, except
space-connections (which don't add any additional unlocks): It knows
that lava isn't accessible until you get to Vulcanus and knows that
Promethium science pack is accessible. It is happy loading new and
existing pY and SA projects, though the pY projects [need a change to
pypostprocessing](pyanodon/pypostprocessing#58).

In order to not break Space science when fixing Promethium science, I
had to implement these, both internally and in the UI:

![image](https://github.com/user-attachments/assets/51fbfe78-3784-41ca-9b11-cd0ea28b625b)
![image](https://github.com/user-attachments/assets/64dac276-6e2e-4051-b646-d0b78bbc68a7)
The vertical bar will connect as many horizontal OR bars as necessary.
Lists that are not separated by an OR bar (almost all of them) use the
standard AND behavior.

I also decided this (1364 pixels high) was bad

![image](https://github.com/user-attachments/assets/7f533d19-c14b-4791-8867-1034918d4151)
and changed it to this instead (down to 970 px)

![image](https://github.com/user-attachments/assets/358b6820-fcdd-4940-8921-bc394a49a97d)
  • Loading branch information
shpaass authored Nov 18, 2024
2 parents fe0f7a1 + f27f705 commit 6b0561f
Show file tree
Hide file tree
Showing 25 changed files with 735 additions and 296 deletions.
2 changes: 1 addition & 1 deletion Yafc.Model/Analysis/Analysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static void ExcludeFromAnalysis<T>(FactorioObject obj) where T : Analysis
}

public static class AnalysisExtensions {
public static bool IsAccessible(this FactorioObject obj) => Milestones.Instance.GetMilestoneResult(obj) != 0;
public static bool IsAccessible(this FactorioObject obj) => Milestones.Instance.GetMilestoneResult(obj)[0];

public static bool IsAccessibleWithCurrentMilestones(this FactorioObject obj) => Milestones.Instance.IsAccessibleWithCurrentMilestones(obj);

Expand Down
40 changes: 1 addition & 39 deletions Yafc.Model/Analysis/AutomationAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,45 +53,7 @@ public override void Compute(Project project, ErrorCollector warnings) {
var dependencies = Dependencies.dependencyList[index];
var automationState = Milestones.Instance.IsAccessibleWithCurrentMilestones(index) ? AutomationStatus.AutomatableNow : AutomationStatus.AutomatableLater;

foreach (var depGroup in dependencies) {
if (!depGroup.flags.HasFlags(DependencyList.Flags.OneTimeInvestment)) {
if (depGroup.flags.HasFlags(DependencyList.Flags.RequireEverything)) {
foreach (var element in depGroup.elements) {
if (state[element] < automationState) {
automationState = state[element];
}
}
}
else {
var localHighest = AutomationStatus.NotAutomatable;

foreach (var element in depGroup.elements) {
if (state[element] > localHighest) {
localHighest = state[element];
}
}

if (localHighest < automationState) {
automationState = localHighest;
}
}
}
else if (automationState == AutomationStatus.AutomatableNow && depGroup.flags == DependencyList.Flags.CraftingEntity) {
// If only character is accessible at current milestones as a crafting entity, don't count the object as currently automatable
bool hasMachine = false;

foreach (var element in depGroup.elements) {
if (element != Database.character?.id && Milestones.Instance.IsAccessibleWithCurrentMilestones(element)) {
hasMachine = true;
break;
}
}

if (!hasMachine) {
automationState = AutomationStatus.AutomatableLater;
}
}
}
automationState = dependencies.IsAutomatable(id => state[id], automationState);

if (automationState == UnknownInQueue) {
automationState = Unknown;
Expand Down
64 changes: 20 additions & 44 deletions Yafc.Model/Analysis/Dependencies.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Yafc.Model;

public interface IDependencyCollector {
void Add(FactorioId[] raw, DependencyList.Flags flags);
void Add(IReadOnlyList<FactorioObject> raw, DependencyList.Flags flags);
}

public struct DependencyList {
public struct DependencyList(FactorioId[] elements, DependencyList.Flags flags) {
[Flags]
public enum Flags {
RequireEverything = 0x100,
Expand All @@ -24,63 +20,43 @@ public enum Flags {
TechnologyPrerequisites = 8 | RequireEverything | OneTimeInvestment,
IngredientVariant = 9,
Hidden = 10,
Location = 11 | OneTimeInvestment,
}

public Flags flags;
public FactorioId[] elements;
public Flags flags = flags;
public FactorioId[] elements = elements;

public DependencyList(IEnumerable<FactorioObject> elements, Flags flags) : this(elements.Select(o => o.id).ToArray(), flags) { }
}

public static class Dependencies {
public static Mapping<FactorioObject, DependencyList[]> dependencyList { get; private set; }
/// <summary>
/// The objects the key requires, organized into useful categories. Some categories are requires-any, others are requires-all.
/// e.g. <c>dependencyList["Item.steel-plate"]</c> will contain the recipes that produce it and the entities that have it as loot.
/// </summary>
public static Mapping<FactorioObject, DependencyNode> dependencyList { get; private set; }
/// <summary>
/// The objects that require the key. e.g. <c>reverseDependencies["Item.steel-plate"]</c> will contain the recipes that consume steel plate.
/// </summary>
public static Mapping<FactorioObject, List<FactorioId>> reverseDependencies { get; private set; }

public static void Calculate() {
dependencyList = Database.objects.CreateMapping<DependencyList[]>();
dependencyList = Database.objects.CreateMapping<DependencyNode>();
reverseDependencies = Database.objects.CreateMapping<List<FactorioId>>();

foreach (var obj in Database.objects.all) {
reverseDependencies[obj] = [];
}

DependencyCollector collector = new DependencyCollector();
List<FactorioObject> temp = [];

foreach (var obj in Database.objects.all) {
obj.GetDependencies(collector, temp);
var packed = collector.Pack();
DependencyNode packed = obj.GetDependencies();
dependencyList[obj] = packed;

foreach (var group in packed) {
foreach (var req in group.elements) {
if (!reverseDependencies[req].Contains(obj.id)) {
reverseDependencies[req].Add(obj.id);
}
foreach (FactorioId req in packed.Flatten()) {
if (!reverseDependencies[req].Contains(obj.id)) {
reverseDependencies[req].Add(obj.id);
}
}
}
}

private class DependencyCollector : IDependencyCollector {
private readonly List<DependencyList> list = [];

public void Add(FactorioId[] raw, DependencyList.Flags flags) => list.Add(new DependencyList { elements = raw, flags = flags });

public void Add(IReadOnlyList<FactorioObject> raw, DependencyList.Flags flags) {
FactorioId[] elems = new FactorioId[raw.Count];

for (int i = 0; i < raw.Count; i++) {
elems[i] = raw[i].id;
}

list.Add(new DependencyList { elements = elems, flags = flags });
}

public DependencyList[] Pack() {
var packed = list.ToArray();
list.Clear();

return packed;
}
}

}
Loading

0 comments on commit 6b0561f

Please sign in to comment.