1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.Graphic.Spritesheet;
25 
26 private:
27 
28 import Dgame.Graphic.Texture;
29 import Dgame.Graphic.Sprite;
30 
31 import Dgame.Math.Vector2;
32 import Dgame.Math.Rect;
33 
34 import Dgame.System.StopWatch;
35 
36 public:
37 
38 /**
39  * SpriteSheet extends Sprite and act like a Texture Atlas.
40  *
41  * Author: Randy Schuett (rswhite4@googlemail.com)
42  */
43 class Spritesheet : Sprite {
44 protected:
45     uint _lastUpdate = 0;
46     uint _execCount = 0;
47 
48 public:
49     /**
50      * The timeout between the slides
51      */
52     uint timeout = 0;
53     /**
54      * The amount of executions of the <b>whole</b> slide
55      * -1 or less means infinite sliding
56      */
57     int numOfExecutions = -1;
58 
59 final:
60     /**
61      * CTor
62      */
63     @nogc
64     this(ref Texture tex) pure nothrow {
65         super(tex);
66     }
67 
68     /**
69      * CTor
70      */
71     @nogc
72     this(ref Texture tex, const Rect texRect) pure nothrow {
73         this(tex);
74 
75         super.setTextureRect(texRect);
76     }
77 
78     /**
79      * Returns the last update which means the last happened slide
80      */
81     @nogc
82     uint lastUpdate() const pure nothrow {
83         return _lastUpdate;
84     }
85 
86     /**
87      * Returns the current execution count
88      */
89     @nogc
90     uint executionCounter() const pure nothrow {
91         return _execCount;
92     }
93 
94     /**
95      * Returns if the execution is completed
96      */
97     @nogc
98     bool isCompleted() const pure nothrow {
99         return this.numOfExecutions >= 0 && _execCount >= this.numOfExecutions;
100     }
101 
102     /**
103      * Slide / move the current view of the Texture,
104      * so that the next view of the Texture will be drawn.
105      * This happens by moving the Texture Rect.
106      *
107      * Note: If you reached the end of the Texture, the procedure starts from scratch.
108      */
109     @nogc
110     void slideTextureRect() nothrow {
111         if (this.isCompleted())
112             return;
113 
114         if ((_lastUpdate + this.timeout) > StopWatch.getTicks())
115             return;
116 
117         _lastUpdate = StopWatch.getTicks();
118 
119         _texRect.x += _texRect.width;
120         if (_texRect.x >= _texture.width) {
121             _texRect.x = 0;
122             _texRect.y += _texRect.height;
123             if (_texRect.y >= _texture.height) {
124                 _texRect.y = 0;
125                 _execCount++;
126             }
127         }
128 
129         // show the last frame if the execution is completed
130         if (this.isCompleted()) {
131             _texRect.x = _texture.width - _texRect.width;
132             _texRect.y = _texture.height - _texRect.height;
133         }
134 
135         _updateVertices();
136     }
137 
138     /**
139      * Selects a specific frame and adapts the position of the texture rect.
140      * The index starts at 0 and line by line.
141      *
142      * Note: The size of your Texture and the size of your Texture-Rect should be divisible without a rest.
143      */
144     @nogc
145     void selectFrame(ubyte index) pure nothrow {
146         immutable uint nof = this.numOfFrames();
147         if (index >= nof)
148             index = index % nof;
149 
150         int sum = index * _texRect.width;
151      
152         int x = 0, y = 0;
153         while (sum >= _texRect.width) {
154             x += _texRect.width;
155             if (x >= _texture.width) {
156                 x = 0;
157                 y += _texRect.height;
158             }
159      
160             sum -= _texRect.width;
161         }
162 
163         _texRect.x = x;
164         _texRect.y = y;
165 
166         _updateVertices();
167     }
168 
169     /**
170      * Returns how many frames this Spritesheet has.
171      * The width / height of the current Texture is divided through
172      * the corresponding width / height of the current Texture-Rect and both values are being multiplied.
173      */
174     @nogc
175     uint numOfFrames() const pure nothrow {
176         return (_texture.width / _texRect.width) * (_texture.height / _texRect.height);
177     }
178 }