Maybe from some event handler, you want to decide what spell to cast. Perhaps you can start with something like this spellcaster:
public class Caster
{
private readonly ICastable[] _spells;
private int _canCastAt;
public Caster(ICastable[] spells)
{
_spells = spells;
_canCastAt = -1;
}
public string GetNextSpellToCast(int currentTime)
{
if (currentTime < _canCastAt)
return null;
for (int i = 0; i < _spells.Length; i++)
{
int durationOfCast = _spells[i].AttemptCast(currentTime);
if (durationOfCast > 0)
{
_canCastAt = currentTime + durationOfCast;
return _spells[i].GetName();
}
}
return null;
}
}
The caster will cast spells:
public interface ICastable
{
string GetName();
int AttemptCast(int msCurrentTime);
}
You described a particular kind of spell:
public class ChanneledSpell : ICastable
{
private readonly string _name;
private readonly int _castDuration;
private readonly int _castCooldown;
private int _lastCast;
public ChanneledSpell(string name, int castDuration, int castCooldown)
{
Debug.Assert(castDuration < castCooldown); // a reasonable assumption the tests makes
_name = name;
_castDuration = castDuration;
_castCooldown = castCooldown;
_lastCast = -_castCooldown;
}
public string GetName()
{
return _name;
}
public int AttemptCast(int msCurrentTime)
{
if (msCurrentTime > _lastCast + _castCooldown)
{
_lastCast = msCurrentTime;
return _castDuration;
}
return 0;
}
}
I see this was marked C++, this answer is C# though I only used language constructs available in C++ so it should be a straightforward translation. What I couldn't translate so easily are some of the tests,
[TestFixture]
public class SpellTest
{
[Test]
public void TestCanCastOnStartup()
{
var sut = new ChanneledSpell(Some.String(), Some.PositiveNonzeroInteger(), Some.PositiveNonzeroInteger());
int result = sut.AttemptCast(Some.PositiveNonzeroInteger());
Assert.IsTrue(CastWasMade(result));
}
[Test]
public void TestCantCastUntilCooldown()
{
int firstCast = Some.PositiveNonzeroInteger();
int duration = Some.PositiveNonzeroInteger();
int cooldown = duration + Some.PositiveNonzeroInteger(); // assuming spell duration is less then cooldown
var sut = new ChanneledSpell(Some.String(), duration, cooldown);
int ignored = sut.AttemptCast(firstCast);
int secondCastAttempt = sut.AttemptCast(firstCast + cooldown - 1);
int thirdCastAttempt = sut.AttemptCast(firstCast + cooldown + 1);
Assert.IsFalse(CastWasMade(secondCastAttempt));
Assert.IsTrue(CastWasMade(thirdCastAttempt));
}
[Test]
public void TestReportsTimeOnCast()
{
int duration = Some.PositiveNonzeroInteger();
int firstCastTime = Some.PositiveNonzeroInteger();
var sut = new ChanneledSpell(Some.String(), duration, Some.PositiveNonzeroInteger());
int firstCastAttempt = sut.AttemptCast(firstCastTime);
Assert.AreEqual(duration, firstCastAttempt);
}
private bool CastWasMade(int result)
{
return result > 0;
}
}