Oh my goodness, I can't believe this worked:
// This is a counter whose range is [0, some_arbitrary_max]
// Setting the max really high mimics a range of [0, infinity); this is also the default
// This can be used for all sorts of applications, including a healthbar
public class Counter {
private int _count;
private int _max;
// Constructor with a preset max
public Counter(int presetMax) {
Max = presetMax;
_count = 0;
}
// Default constructor
public Counter() {
_max = int.MaxValue;
_count = 0;
}
// Increment the count by an arbitrary value; decrement using a negative value
public void IncrementCount(int amt) {
int newCount = _count + amt;
if (newCount < 0) _count = 0;
else if (newCount > _max) _count = _max;
else _count = newCount;
}
// Additional functions for zeroing or maxing out the counter
public void ZeroOutCount() { Count = 0; }
public void MaxOutCount() { Count = Max; }
// Increment the max by an arbitrary value; decrement using a negative value
// If the max ever becomes less than the count, the count should be reassigned
// to be the same as the max; this ensures the count stays within [0, max]
public void IncrementMax(int amt) {
if (_max + amt < 0) _count = 0;
else _max += amt;
if (_max <= _count) _count = _max;
}
// Setter-getter for the max; this can also force the count to be any value, but
// will readjust the count if the max falls below the count to keep the count within
// the range [0, max]
public int Max {
set {
if (value >= 0) _max = value;
if (_max < _count) _count = _max;
}
get { return _max; }
}
// Setter-getter for the count; this can also force the count to be any value
public int Count {
set { if (value >= 0) _count = value; }
get { return _count; }
}
// This is the same as the regular counter but instead of having a count
// within the range [0, max], there are two counts (count1 and count2) such that
// 0 >= count1 >= count2 >= max; since this class encapsulates two instances of
// a counter, any and all calculations are handled by the individual counters
public class Bicounter {
Counter _ctr2; // Effective range of [0, max]
Counter _ctr1; // Effective range of [0, ctr2.Count]
public Bicounter(int presetMax) {
_ctr2 = new Counter(presetMax);
_ctr1 = new Counter(_ctr2.Count);
}
public Bicounter() {
_ctr2 = new Counter();
_ctr1 = new Counter(_ctr2.Count);
}
public void IncrementCount1(int amt) {
_ctr1.IncrementCount(amt);
}
public void IncrementCount2(int amt) {
_ctr2.IncrementCount(amt);
_ctr1.Max = _ctr2.Count;
}
public void ZeroOutCount() {
_ctr2.ZeroOutCount();
_ctr1.ZeroOutCount();
}
public void MaxOutCount() {
_ctr2.MaxOutCount();
_ctr1.Max = _ctr2.Max;
_ctr1.MaxOutCount();
}
public int Max {
set { _ctr2.Max = value; }
get { return _ctr2.Max; }
}
public int Count2 {
set { _ctr2.Count = value; _ctr1.Max = _ctr2.Count; }
get { return _ctr2.Count; }
}
public int Count1 {
set { _ctr1.Count = value; }
get { return _ctr1.Count; }
}
}
I may need more tests, but this is the healthbar-inside-of-a-healthbar thing I was talking about a few weeks ago such that 0 >= count_1 >= count_2 >= max, and I achieved it by encapsulating two separate counters inside of a bicounter class.
No, I'm not gonna think about making a tricounter or an ncounter; even though the code can be generalised, I don't have a use for anything beyond a bicounter. No, I have no idea how this would be done using ECS since the counter is an overly generalized healthbar. I can't justify using Unity's data oriented tech stack when I have a fairly puny project where everything would be represented by a UI.
If this was a healthbar, then the counter works fine. However, I have a situation where I may need the bicounter instead; EG, instead of distinguishing between occupied buildings and unoccupied buildings, I have to distinguish between unused plots of land, plots of land with an unoccupied building, and plots of land with an occupied building.
Now that I think about it, a bicounter may not be needed if a plot of land can contain more than one building; instead, it'd have to be two separate counters, one for used and unused plots of land, with a means of getting the number of individual buildings, and another for occupied and unoccupied buildings.