Skip to content

Commit

Permalink
Support more than 64 milestones
Browse files Browse the repository at this point in the history
Introduces a Bits class that is able to handle a dynamic amount of bits,
instead of the 64 bits stored in the ulong.
  • Loading branch information
veger committed Feb 18, 2023
1 parent edf0729 commit 70a0b0a
Show file tree
Hide file tree
Showing 12 changed files with 992 additions and 97 deletions.
10 changes: 5 additions & 5 deletions YAFC/Widgets/ObjectTooltip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ private void BuildHeader(ImGui gui)
if (extendHeader && !(target is Goods))
name = name +" (" + target.target.type + ")";
gui.BuildText(name, Font.header, true);
var milestoneMask = Milestones.Instance.milestoneResult[target.target];
if (milestoneMask > 1)
var milestoneMask = Milestones.Instance.GetMilestoneResult(target.target);
if (milestoneMask.HighestBitSet() > 0)
{
var spacing = MathF.Min(22f / Milestones.Instance.currentMilestones.Length - 1f, 0f);
using (gui.EnterRow(spacing))
{
var mask = 2ul;
var maskBit = 1;
foreach (var milestone in Milestones.Instance.currentMilestones)
{
if ((milestoneMask & mask) != 0)
if (milestoneMask[maskBit])
gui.BuildIcon(milestone.icon, 1f, SchemeColor.Source);
mask <<= 1;
maskBit++;
}
}
}
Expand Down
13 changes: 6 additions & 7 deletions YAFC/Windows/MilestonesEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,24 @@ private void AddMilestone(FactorioObject obj)
MessageBox.Show(null, "Milestone already exists", "Ok");
return;
}
var lockedMask = Milestones.Instance.milestoneResult[obj];
if (lockedMask == 0)
var lockedMask = Milestones.Instance.GetMilestoneResult(obj);
if (lockedMask.IsClear())
{
settings.RecordUndo().milestones.Add(obj);
}
else
{
var bestIndex = 0;
for (var i = 1; i < 64; i++)
for (var i = 1; i < lockedMask.length; i++)
{
var mask = (1ul << i);
if ((lockedMask & mask) != 0)
if (lockedMask[i])
{
lockedMask &= ~mask;
lockedMask[i] = false;
var milestone = Milestones.Instance.currentMilestones[i - 1];
var index = settings.milestones.IndexOf(milestone);
if (index >= bestIndex)
bestIndex = index + 1;
if (lockedMask == 0)
if (lockedMask.IsClear())
break;
}
}
Expand Down
4 changes: 2 additions & 2 deletions YAFC/Windows/MilestonesPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ private static void MilestoneDrawer(ImGui gui, FactorioObject element, int index
{
if (!unlocked)
{
var massUnlock = Milestones.Instance.milestoneResult[element];
var massUnlock = Milestones.Instance.GetMilestoneResult(element);
var subIndex = 0;
settings.SetFlag(element, ProjectPerItemFlags.MilestoneUnlocked, true);
foreach (var milestone in settings.milestones)
{
subIndex++;
if ((massUnlock & (1ul << subIndex)) != 0)
if (massUnlock[subIndex])
settings.SetFlag(milestone, ProjectPerItemFlags.MilestoneUnlocked, true);
}
}
Expand Down
47 changes: 36 additions & 11 deletions YAFCmodel.Tests/Analysis/Milestones.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,39 @@ namespace YAFC.Model.Tests
{
public class MilestonesTests
{
private static Bits createBits(ulong value)
{
var bitsType = typeof(Bits);
var bitsData = bitsType.GetField("data", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var bitsLength = bitsType.GetField("_length", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

var bits = new Bits();
bitsData.SetValue(bits, new ulong[] { value });
bitsLength.SetValue(bits, sizeof(ulong));
bitsLength.SetValue(bits, bits.HighestBitSet() + 1);

return bits;
}
private static Milestones setupMilestones(ulong result, ulong mask, out FactorioObject factorioObj)
{
factorioObj = new Technology();
var milestoneResult = new Mapping<FactorioObject, ulong>(new FactorioIdRange<FactorioObject>(0, 1, new List<FactorioObject>() {
var milestoneResult = new Mapping<FactorioObject, Bits>(new FactorioIdRange<FactorioObject>(0, 1, new List<FactorioObject>() {
factorioObj
}));
milestoneResult[factorioObj] = result;
milestoneResult[factorioObj] = createBits(result);


var milestonesType = typeof(Milestones);
var milestonesLockedMask = milestonesType.GetProperty("lockedMask");
var milestoneResultField = milestonesType.GetField("milestoneResult", BindingFlags.NonPublic | BindingFlags.Instance);

var milestones = new Milestones()
{
milestoneResult = milestoneResult,
currentMilestones = new FactorioObject[] { factorioObj }
};

var milestonesType = typeof(Milestones);
var milestonesLockedMask = milestonesType.GetProperty("lockedMask");
milestonesLockedMask.SetValue(milestones, mask);
milestoneResultField.SetValue(milestones, milestoneResult);
milestonesLockedMask.SetValue(milestones, createBits(mask));

return milestones;
}
Expand Down Expand Up @@ -55,9 +71,9 @@ public void IsAccessibleAtNextMilestone_WhenGivenMilestones_ShouldReturnCorrectV
}

[Theory]
[InlineData(false, ~0ul)] // all bits set (nothing gets masked)
[InlineData(true, ~0ul & ~2ul)] // all bits set, except bit 1 (for reasons not bit 0, even if the FIRST milestone has its flag set?!)
public void GetLockedMaskFromProject_ShouldCalculateMask(bool unlocked, ulong expectedResult)
[InlineData(false, new int[] { })] // all bits set (nothing gets masked)
[InlineData(true, new int[] { 1 })] // all bits set, except bit 1 (for reasons not bit 0, even if the FIRST milestone has its flag set?!)
public void GetLockedMaskFromProject_ShouldCalculateMask(bool unlocked, int[] bitsCleared)
{
var milestonesType = typeof(Milestones);
var getLockedMaskFromProject = milestonesType.GetMethod("GetLockedMaskFromProject", BindingFlags.NonPublic | BindingFlags.Instance);
Expand All @@ -76,12 +92,21 @@ public void GetLockedMaskFromProject_ShouldCalculateMask(bool unlocked, ulong ex
itemFlagsField.SetValue(project.settings, flags);
}


projectField.SetValue(milestones, project);

getLockedMaskFromProject.Invoke(milestones, null);
var lockedBits = milestones.lockedMask;

Assert.Equal(expectedResult, milestones.lockedMask);
var index = 0;
for (int i = 0; i < lockedBits.length; i++)
{
var expectSet = index == bitsCleared.Length || bitsCleared[index] != i;
Assert.True(expectSet == lockedBits[i], "bit " + i + " is expected to be " + (expectSet ? "set" : "cleared"));
if (index < bitsCleared.Length && bitsCleared[index] == i)
{
index++;
}
}
}

[Theory]
Expand Down
Loading

0 comments on commit 70a0b0a

Please sign in to comment.