1 /**
2     Tweening
3 
4     Copyright: (c) Enalye 2019
5     License: Zlib
6     Authors: Enalye
7 */
8 
9 module atelier.core.timer;
10 
11 import atelier.common;
12 
13 /**
14 	Simple updatable timer. \
15 	Start with start() and update with update(Deltatime). \
16 	Returns a value between 0 and 1.
17 */
18 struct Timer {
19     /// Change the way Timer behave. \
20     /// once: Will run from 0 to 1 in one duration then stop. \
21     /// reverse: Will run from 1 to 0 in one duration then stop. \
22     /// loop: Will run from 0 to 1 in one duration then restart from 0 again, etc. \
23     /// loopReverse: Will run from 1 to 0 in one duration then restart from 1 again, etc. \
24     /// bounce: Will run from 0 to 1 in one duration then from 1 to 0, etc. \
25     /// bounceReverse: Will run from 1 to 0 in one duration then from 0 to 1, etc.
26     enum Mode {
27         once,
28         reverse,
29         loop,
30         loopReverse,
31         bounce,
32         bounceReverse
33     }
34 
35     private {
36         float _time = 0f, _duration = 1f;
37         bool _isRunning = false, _isReversed = false;
38         Mode _mode = Mode.once;
39     }
40 
41     @property {
42         /// The relative time elapsed between 0 and 1.
43         float value01() const {
44             return _time;
45         }
46 
47         /// Time elapsed between 0 and the max duration.
48         float value() const {
49             return _time * _duration;
50         }
51 
52         /// Duration in seconds from witch the timer goes from 0 to 1 (framerate dependent). \
53         /// Only positive non-null values.
54         float duration() const {
55             return _duration;
56         }
57         /// Ditto
58         float duration(float v) {
59             return _duration = v;
60         }
61 
62         /// Is the timer currently running ?
63         bool isRunning() const {
64             return _isRunning;
65         }
66 
67         /// Current behavior of the timer.
68         Mode mode() const {
69             return _mode;
70         }
71         /// Ditto
72         Mode mode(Mode v) {
73             return _mode = v;
74         }
75     }
76 
77     /// Immediatly starts the timer. \
78     /// Note that loop and bounce behaviours will never stop until you tell him to.
79     void start() {
80         _isRunning = true;
81         reset();
82     }
83 
84     /// Immediatly starts the timer with the specified running time. \
85     /// Note that loop and bounce behaviours will never stop until you tell him to.
86     void start(float duration_) {
87         _isRunning = true;
88         duration(duration_);
89         reset();
90     }
91 
92     /// Immediatly stops the timer and resets it.
93     void stop() {
94         _isRunning = false;
95         reset();
96     }
97 
98     /// Interrupts the timer without resetting it.
99     void pause() {
100         _isRunning = false;
101     }
102 
103     /// Resumes the timer from where it was stopped.
104     void resume() {
105         _isRunning = true;
106     }
107 
108     /// Goes back to starting settings.
109     void reset() {
110         final switch (_mode) with (Mode) {
111         case once:
112             _time = 0f;
113             _isReversed = false;
114             break;
115         case reverse:
116             _time = 1f;
117             _isReversed = false;
118             break;
119         case loop:
120             _time = 0f;
121             _isReversed = false;
122             break;
123         case loopReverse:
124             _time = 1f;
125             _isReversed = false;
126             break;
127         case bounce:
128             _time = 0f;
129             _isReversed = false;
130             break;
131         case bounceReverse:
132             _time = 1f;
133             _isReversed = false;
134             break;
135         }
136     }
137 
138     /// Update with the current deltatime (~1)
139     /// If you don't call update, the timer won't advance.
140     void update(float deltaTime) {
141         if (!_isRunning)
142             return;
143         if (_duration <= 0f) {
144             _time = _isReversed ? 0f : 1f;
145             _isRunning = false;
146             return;
147         }
148         const float stepInterval = deltaTime / (getNominalFPS() * _duration);
149         final switch (_mode) with (Mode) {
150         case once:
151             if (_time < 1f)
152                 _time += stepInterval;
153             if (_time >= 1f) {
154                 _time = 1f;
155                 _isRunning = false;
156             }
157             break;
158         case reverse:
159             if (_time > 0f)
160                 _time -= stepInterval;
161             if (_time <= 0f) {
162                 _time = 0f;
163                 _isRunning = false;
164             }
165             break;
166         case loop:
167             if (_time < 1f)
168                 _time += stepInterval;
169             if (_time >= 1f)
170                 _time = (_time - 1f) + stepInterval;
171             break;
172         case loopReverse:
173             if (_time > 0f)
174                 _time -= stepInterval;
175             if (_time <= 0f)
176                 _time = (1f - _time) - stepInterval;
177             break;
178         case bounce:
179             if (_isReversed) {
180                 if (_time > 0f)
181                     _time -= stepInterval;
182                 if (_time <= 0f) {
183                     _time = -(_time - stepInterval);
184                     _isReversed = false;
185                 }
186             }
187             else {
188                 if (_time < 1f)
189                     _time += stepInterval;
190                 if (_time >= 1f) {
191                     _time = 1f - ((_time - 1f) + stepInterval);
192                     _isReversed = true;
193                 }
194             }
195             break;
196         case bounceReverse:
197             if (_isReversed) {
198                 if (_time < 1f)
199                     _time += stepInterval;
200                 if (_time >= 1f) {
201                     _time = 1f - ((_time - 1f) + stepInterval);
202                     _isReversed = false;
203                 }
204             }
205             else {
206                 if (_time > 0f)
207                     _time -= stepInterval;
208                 if (_time <= 0f) {
209                     _time = -(_time - stepInterval);
210                     _isReversed = true;
211                 }
212             }
213             break;
214         }
215     }
216 }