Frame
: A logical update, 100ms delta.
Action
: A thing to do (e.g. attack)
Length
: The number of frames it takes to complete one action, as a float because we want to support fractions (this is the 3 and buffed 1.5 from your example).
On Start New Action Type:
FramesToAction = Length;
Logical Frame:
FramesToAction -= 1;
if (FramesToAction <= 0) {
DoAction();
FramesToAction += Length;
}
This logic will give the behaviour you desire, might need to be careful Length always is >= 1.