Stefan Schuermans commited on 2019-07-14 17:51:57
Showing 3 changed files, with 565 additions and 0 deletions.
... | ... |
@@ -0,0 +1,388 @@ |
1 |
+/* Blinker |
|
2 |
+ Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org> |
|
3 |
+ Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html |
|
4 |
+ a blinkenarea.org project */ |
|
5 |
+ |
|
6 |
+#include <cmath> |
|
7 |
+#include <stdlib.h> |
|
8 |
+#include <string> |
|
9 |
+#include <vector> |
|
10 |
+ |
|
11 |
+#include <BlinkenLib/BlinkenFrame.h> |
|
12 |
+ |
|
13 |
+#include "File.h" |
|
14 |
+#include "Format.h" |
|
15 |
+#include "FormatFile.h" |
|
16 |
+#include "Game.h" |
|
17 |
+#include "Mgrs.h" |
|
18 |
+#include "Module.h" |
|
19 |
+#include "NameFile.h" |
|
20 |
+#include "OpConn.h" |
|
21 |
+#include "OpConnIf.h" |
|
22 |
+#include "OpReqIf.h" |
|
23 |
+#include "OutStreamFile.h" |
|
24 |
+#include "Tetris.h" |
|
25 |
+#include "Time.h" |
|
26 |
+#include "TimeCallee.h" |
|
27 |
+#include "UIntFile.h" |
|
28 |
+ |
|
29 |
+namespace Blinker { |
|
30 |
+ |
|
31 |
+/** |
|
32 |
+ * @brief constructor |
|
33 |
+ * @param[in] name module name |
|
34 |
+ * @param[in] mgrs managers |
|
35 |
+ * @param[in] dirBase base directory |
|
36 |
+ */ |
|
37 |
+Tetris::Tetris(const std::string &name, Mgrs &mgrs, const Directory &dirBase): |
|
38 |
+ Game(name, mgrs, dirBase), |
|
39 |
+ m_fileStoneColor(dirBase.getFile("stoneColor")), |
|
40 |
+ m_fileDelay(dirBase.getFile("delay")), |
|
41 |
+ m_fileStartSound(dirBase.getFile("startSound")), |
|
42 |
+ m_stoneColor(), |
|
43 |
+ m_delay(c_delayDescr.default_), |
|
44 |
+ m_pConn(NULL), |
|
45 |
+ m_stone(-1), m_rot(-1), m_posX(-1), m_posY(-1), m_field() |
|
46 |
+{ |
|
47 |
+ // open operator connection interfaces for player |
|
48 |
+ m_mgrs.m_opMgr.open(m_name, this); |
|
49 |
+} |
|
50 |
+ |
|
51 |
+/// virtual destructor |
|
52 |
+Tetris::~Tetris() |
|
53 |
+{ |
|
54 |
+ // close operator connection interface |
|
55 |
+ m_mgrs.m_opMgr.close(m_name); |
|
56 |
+ |
|
57 |
+ // close open operator connection |
|
58 |
+ if (m_pConn) { |
|
59 |
+ m_pConn->close(); |
|
60 |
+ m_pConn = NULL; |
|
61 |
+ } |
|
62 |
+} |
|
63 |
+ |
|
64 |
+/// check for update of configuration (derived game), return true on update |
|
65 |
+bool Tetris::updateConfigGame() |
|
66 |
+{ |
|
67 |
+ bool ret = false; |
|
68 |
+ |
|
69 |
+ // color file was modified -> convert color, return true for update |
|
70 |
+ // cfg value file was updated -> read new cfg value, return true for update |
|
71 |
+ if (colorUpdate(m_fileStoneColor, m_stoneColor) || |
|
72 |
+ valueUpdate(m_fileDelay, c_delayDescr, m_delay)) { |
|
73 |
+ ret = true; |
|
74 |
+ } |
|
75 |
+ |
|
76 |
+ // sound name file was modified -> re-read sound name, no other update needed |
|
77 |
+ soundUpdate(m_fileStartSound); |
|
78 |
+ |
|
79 |
+ return ret; |
|
80 |
+} |
|
81 |
+ |
|
82 |
+/** |
|
83 |
+ * @brief check if accepting new operator connection is possible |
|
84 |
+ * @param[in] name operator interface name |
|
85 |
+ * @return if accepting new connection is possible |
|
86 |
+ */ |
|
87 |
+bool Tetris::acceptNewOpConn(const std::string &name) |
|
88 |
+{ |
|
89 |
+ (void)name; |
|
90 |
+ |
|
91 |
+ // accept player if no one there yet |
|
92 |
+ return ! m_pConn; |
|
93 |
+} |
|
94 |
+ |
|
95 |
+/** |
|
96 |
+ * @brief new operator connection |
|
97 |
+ * @param[in] name operator interface name |
|
98 |
+ * @param[in] pConn operator connection object |
|
99 |
+ * |
|
100 |
+ * The new connection may not yet be used for sending inside this callback. |
|
101 |
+ */ |
|
102 |
+void Tetris::newOpConn(const std::string &name, OpConn *pConn) |
|
103 |
+{ |
|
104 |
+ (void)name; |
|
105 |
+ |
|
106 |
+ // player arrives and starts game |
|
107 |
+ if (! m_pConn) { |
|
108 |
+ m_pConn = pConn; |
|
109 |
+ requestOpConnSound(m_pConn, m_fileStartSound); |
|
110 |
+ activate(); |
|
111 |
+ } |
|
112 |
+ // close imcoming connection as soon as possible, nothing else happens |
|
113 |
+ else { |
|
114 |
+ requestOpConnClose(pConn); |
|
115 |
+ return; |
|
116 |
+ } |
|
117 |
+} |
|
118 |
+ |
|
119 |
+/** |
|
120 |
+ * @brief key command received on operator connection |
|
121 |
+ * @param[in] pConn operator connection object |
|
122 |
+ * @param[in] key key that was pressed |
|
123 |
+ */ |
|
124 |
+void Tetris::opConnRecvKey(OpConn *pConn, char key) |
|
125 |
+{ |
|
126 |
+ // hash -> hang up |
|
127 |
+ if (key == '#') { |
|
128 |
+ opConnClose(pConn); |
|
129 |
+ pConn->close(); |
|
130 |
+ return; |
|
131 |
+ } |
|
132 |
+ |
|
133 |
+ // star -> inform player about game |
|
134 |
+ if (key == '*') { |
|
135 |
+ playOpConnSound(pConn, m_fileStartSound); |
|
136 |
+ return; |
|
137 |
+ } |
|
138 |
+ |
|
139 |
+ // normal keys for controlling game |
|
140 |
+ |
|
141 |
+ // TODO |
|
142 |
+} |
|
143 |
+ |
|
144 |
+/** |
|
145 |
+ * @brief play command received on operator connection |
|
146 |
+ * @param[in] pConn operator connection object |
|
147 |
+ * @param[in] sound name of sound to play |
|
148 |
+ */ |
|
149 |
+void Tetris::opConnRecvPlay(OpConn *pConn, const std::string &sound) |
|
150 |
+{ |
|
151 |
+ (void)pConn; |
|
152 |
+ (void)sound; |
|
153 |
+} |
|
154 |
+ |
|
155 |
+/** |
|
156 |
+ * @brief operator connection is closed |
|
157 |
+ * @param[in] pConn operator connection object |
|
158 |
+ * |
|
159 |
+ * The connection may not be used for sending any more in this callback. |
|
160 |
+ */ |
|
161 |
+void Tetris::opConnClose(OpConn *pConn) |
|
162 |
+{ |
|
163 |
+ // remove coperator connection from requests (if it was in) |
|
164 |
+ forgetOpConn(pConn); |
|
165 |
+ |
|
166 |
+ // player leaves -> deactivate game |
|
167 |
+ if (pConn == m_pConn) { |
|
168 |
+ m_pConn = NULL; |
|
169 |
+ deactivate(); |
|
170 |
+ } |
|
171 |
+} |
|
172 |
+ |
|
173 |
+/// re-initialize game (e.g. due to config change) |
|
174 |
+void Tetris::reinitialize() |
|
175 |
+{ |
|
176 |
+ // convert colors |
|
177 |
+ color2data(m_fileStoneColor, m_stoneColor); |
|
178 |
+ // get values |
|
179 |
+ valueFromFile(m_fileDelay, c_delayDescr, m_delay); |
|
180 |
+ |
|
181 |
+ // initialize field: empty |
|
182 |
+ m_field.clear(); |
|
183 |
+ m_field.resize(m_height * m_width, -1); |
|
184 |
+ |
|
185 |
+ // start with new stone |
|
186 |
+ newStone(); |
|
187 |
+ |
|
188 |
+ // redraw image and send frame |
|
189 |
+ redraw(); |
|
190 |
+ |
|
191 |
+ // request first time step if needed |
|
192 |
+ planTimeStep(); |
|
193 |
+} |
|
194 |
+ |
|
195 |
+/// redraw current game image, expected to call sendFrame() at end |
|
196 |
+void Tetris::redraw() |
|
197 |
+{ |
|
198 |
+ // draw background |
|
199 |
+ rectFill(0, m_height, 0, m_width, m_backgroundColor); |
|
200 |
+ |
|
201 |
+ // draw fixed pixels |
|
202 |
+ for (int y = 0, i = 0; y < m_height; ++y) { |
|
203 |
+ for (int x = 0; x < m_width; ++x, ++i) { |
|
204 |
+ if (m_field.at(i) >= 0) { |
|
205 |
+ pixel(y, x, m_stoneColor); |
|
206 |
+ } |
|
207 |
+ } |
|
208 |
+ } |
|
209 |
+ |
|
210 |
+ // draw current stone |
|
211 |
+ drawStone(m_stone, m_rot, m_posY, m_posX); |
|
212 |
+ |
|
213 |
+ // send updated image buffer as frame |
|
214 |
+ sendFrame(); |
|
215 |
+} |
|
216 |
+ |
|
217 |
+/// process next time step of game |
|
218 |
+void Tetris::timeStep() |
|
219 |
+{ |
|
220 |
+ // FIXME |
|
221 |
+ if (m_posY >= m_height + 2) { |
|
222 |
+ newStone(); |
|
223 |
+ } |
|
224 |
+ wipeStone(m_stone, m_rot, m_posY, m_posX); |
|
225 |
+ m_posY += 1; |
|
226 |
+ drawStone(m_stone, m_rot, m_posY, m_posX); |
|
227 |
+ |
|
228 |
+ // send updated image buffer as frame |
|
229 |
+ sendFrame(); |
|
230 |
+ |
|
231 |
+ // request next time step |
|
232 |
+ planTimeStep(); |
|
233 |
+} |
|
234 |
+ |
|
235 |
+/// set up a new stone |
|
236 |
+void Tetris::newStone() |
|
237 |
+{ |
|
238 |
+ // random stone, random rotation |
|
239 |
+ m_stone = rand() % c_stoneCnt; |
|
240 |
+ m_rot = rand() % c_rotCnt; |
|
241 |
+ |
|
242 |
+ // postion: two pixels above top middle |
|
243 |
+ m_posX = (m_width - 1) / 2; |
|
244 |
+ m_posY = -2; |
|
245 |
+} |
|
246 |
+ |
|
247 |
+/// set time for next time step of game - or unset if not needed |
|
248 |
+void Tetris::planTimeStep() |
|
249 |
+{ |
|
250 |
+ // no time call needed if not active |
|
251 |
+ if (! isActive()) { |
|
252 |
+ unsetTimeStep(); |
|
253 |
+ return; |
|
254 |
+ } |
|
255 |
+ |
|
256 |
+ // compute interval based on score and bounce count |
|
257 |
+ float interval = 1e-3f * m_delay; |
|
258 |
+ |
|
259 |
+ // request next time call |
|
260 |
+ Time stepTime; |
|
261 |
+ stepTime.fromFloatSec(interval); |
|
262 |
+ setTimeStep(Time::now() + stepTime); |
|
263 |
+} |
|
264 |
+ |
|
265 |
+/// check if stone fits at position |
|
266 |
+bool Tetris::checkStoneFit(int stone, int rot, int y, int x) const |
|
267 |
+{ |
|
268 |
+ // get rotation of stone |
|
269 |
+ if (! checkLimitInt(stone, 0, c_stoneCnt -1) || |
|
270 |
+ ! checkLimitInt(rot, 0, c_rotCnt - 1)) { |
|
271 |
+ return false; // invalid stone or rotation -> does not fit |
|
272 |
+ } |
|
273 |
+ RotStone const &rotStone = c_stones[stone].rot[rot]; |
|
274 |
+ |
|
275 |
+ // check pixels |
|
276 |
+ for (int p = 0; p < c_pixelCnt; ++p) { |
|
277 |
+ int py = y + rotStone.pixels[p].y; |
|
278 |
+ int px = x + rotStone.pixels[p].x; |
|
279 |
+ if (py >= m_height - 1 || ! checkLimitInt(px, 0, m_width - 1)) { |
|
280 |
+ return false; // outside field (except at top) -> does not fit |
|
281 |
+ } |
|
282 |
+ int pi = py *m_width + px; |
|
283 |
+ if (m_field.at(pi) >= 0) { |
|
284 |
+ return false; // occupixed pixel -> does not fit |
|
285 |
+ } |
|
286 |
+ } |
|
287 |
+ |
|
288 |
+ // all checks passed -> stone fits |
|
289 |
+ return true; |
|
290 |
+} |
|
291 |
+ |
|
292 |
+/// draw a stone to image buffer |
|
293 |
+void Tetris::drawStone(int stone, int rot, int y, int x) |
|
294 |
+{ |
|
295 |
+ colorStone(stone, rot, y, x, m_stoneColor); |
|
296 |
+} |
|
297 |
+ |
|
298 |
+/// wipe a stone from image buffer (i.e. replace it with background color) |
|
299 |
+void Tetris::wipeStone(int stone, int rot, int y, int x) |
|
300 |
+{ |
|
301 |
+ colorStone(stone, rot, y, x, m_backgroundColor); |
|
302 |
+} |
|
303 |
+ |
|
304 |
+/// set shape of stone to color in image buffer |
|
305 |
+void Tetris::colorStone(int stone, int rot, int y, int x, |
|
306 |
+ ColorData const &color) |
|
307 |
+{ |
|
308 |
+ // get rotation of stone |
|
309 |
+ if (! checkLimitInt(stone, 0, c_stoneCnt -1) || |
|
310 |
+ ! checkLimitInt(rot, 0, c_rotCnt - 1)) { |
|
311 |
+ return; // invalid stone or rotation -> nothing to do |
|
312 |
+ } |
|
313 |
+ RotStone const &rotStone = c_stones[stone].rot[rot]; |
|
314 |
+ |
|
315 |
+ // color pixels |
|
316 |
+ for (int p = 0; p < c_pixelCnt; ++p) { |
|
317 |
+ pixel(y + rotStone.pixels[p].y, x + rotStone.pixels[p].x, color); |
|
318 |
+ } |
|
319 |
+} |
|
320 |
+ |
|
321 |
+/// stone data |
|
322 |
+Tetris::Stone const Tetris::c_stones[7] = { |
|
323 |
+ // the I |
|
324 |
+ { { |
|
325 |
+ { { { -2, 0 }, { -1, 0 }, { 0, 0 }, { 1, 0 } } }, |
|
326 |
+ { { { 0, -2 }, { 0, -1 }, { 0, 0 }, { 0, 1 } } }, |
|
327 |
+ { { { -2, 0 }, { -1, 0 }, { 0, 0 }, { 1, 0 } } }, |
|
328 |
+ { { { 0, -2 }, { 0, -1 }, { 0, 0 }, { 0, 1 } } }, |
|
329 |
+ } }, |
|
330 |
+ // the L |
|
331 |
+ { { |
|
332 |
+ { { { 1, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 } } }, |
|
333 |
+ { { { 0, -1 }, { 0, 0 }, { 0, 1 }, { 1, 1 } } }, |
|
334 |
+ { { { -1, 0 }, { 0, 0 }, { 1, 0 }, { -1, 1 } } }, |
|
335 |
+ { { { -1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } } }, |
|
336 |
+ } }, |
|
337 |
+ // the J |
|
338 |
+ { { |
|
339 |
+ { { { -1, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 } } }, |
|
340 |
+ { { { 0, -1 }, { 1, -1 }, { 0, 0 }, { 0, 1 } } }, |
|
341 |
+ { { { -1, 0 }, { 0, 0 }, { 1, 0 }, { 1, 1 } } }, |
|
342 |
+ { { { 0, -1 }, { 0, 0 }, { -1, 1 }, { 0, 1 } } }, |
|
343 |
+ } }, |
|
344 |
+ // the T |
|
345 |
+ { { |
|
346 |
+ { { { 0, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 } } }, |
|
347 |
+ { { { 0, -1 }, { 0, 0 }, { 1, 0 }, { 0, 1 } } }, |
|
348 |
+ { { { -1, 0 }, { 0, 0 }, { 1, 0 }, { 0, 1 } } }, |
|
349 |
+ { { { 0, -1 }, { -1, 0 }, { 0, 0 }, { 0, 1 } } }, |
|
350 |
+ } }, |
|
351 |
+ // the O |
|
352 |
+ { { |
|
353 |
+ { { { 0, -1 }, { 1, -1 }, { 0, 0 }, { 1, 0 } } }, |
|
354 |
+ { { { 0, -1 }, { 1, -1 }, { 0, 0 }, { 1, 0 } } }, |
|
355 |
+ { { { 0, -1 }, { 1, -1 }, { 0, 0 }, { 1, 0 } } }, |
|
356 |
+ { { { 0, -1 }, { 1, -1 }, { 0, 0 }, { 1, 0 } } }, |
|
357 |
+ } }, |
|
358 |
+ // the Z |
|
359 |
+ { { |
|
360 |
+ { { { -1, -1 }, { 0, -1 }, { 0, 0 }, { 1, 0 } } }, |
|
361 |
+ { { { 0, -1 }, { -1, 0 }, { 0, 0 }, { -1, 1 } } }, |
|
362 |
+ { { { -1, -1 }, { 0, -1 }, { 0, 0 }, { 1, 0 } } }, |
|
363 |
+ { { { 0, -1 }, { -1, 0 }, { 0, 0 }, { -1, 1 } } }, |
|
364 |
+ } }, |
|
365 |
+ // the S |
|
366 |
+ { { |
|
367 |
+ { { { 0, -1 }, { 1, -1 }, { -1, 0 }, { 0, 0 } } }, |
|
368 |
+ { { { -1, -1 }, { -1, 0 }, { 0, 0 }, { 0, 1 } } }, |
|
369 |
+ { { { 0, -1 }, { 1, -1 }, { -1, 0 }, { 0, 0 } } }, |
|
370 |
+ { { { -1, -1 }, { -1, 0 }, { 0, 0 }, { 0, 1 } } }, |
|
371 |
+ } }, |
|
372 |
+}; |
|
373 |
+ |
|
374 |
+/// number of stones |
|
375 |
+int const Tetris::c_stoneCnt = sizeof(Tetris::c_stones) / |
|
376 |
+ sizeof(Tetris::c_stones[0]); |
|
377 |
+/// number of rotations per stone |
|
378 |
+int const Tetris::c_rotCnt = sizeof(Tetris::Stone::rot) / |
|
379 |
+ sizeof(Tetris::Stone::rot[0]); |
|
380 |
+/// number of pixels per stone |
|
381 |
+int const Tetris::c_pixelCnt = sizeof(Tetris::RotStone::pixels) / |
|
382 |
+ sizeof(Tetris::RotStone::pixels[0]); |
|
383 |
+ |
|
384 |
+/// descriptor for delay value |
|
385 |
+Tetris::ValueDescr const Tetris::c_delayDescr = { 200, 100, 500 }; |
|
386 |
+ |
|
387 |
+} // namespace Blinker |
|
388 |
+ |
... | ... |
@@ -0,0 +1,175 @@ |
1 |
+/* Blinker |
|
2 |
+ Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org> |
|
3 |
+ Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html |
|
4 |
+ a blinkenarea.org project */ |
|
5 |
+ |
|
6 |
+#ifndef BLINKER_TETRIS_H |
|
7 |
+#define BLINKER_TETRIS_H |
|
8 |
+ |
|
9 |
+#include <string> |
|
10 |
+#include <vector> |
|
11 |
+ |
|
12 |
+#include <BlinkenLib/BlinkenFrame.h> |
|
13 |
+ |
|
14 |
+#include "Color.h" |
|
15 |
+#include "ColorFile.h" |
|
16 |
+#include "File.h" |
|
17 |
+#include "Format.h" |
|
18 |
+#include "FormatFile.h" |
|
19 |
+#include "Game.h" |
|
20 |
+#include "Mgrs.h" |
|
21 |
+#include "Module.h" |
|
22 |
+#include "NameFile.h" |
|
23 |
+#include "OpConn.h" |
|
24 |
+#include "OpConnIf.h" |
|
25 |
+#include "OpReqIf.h" |
|
26 |
+#include "OutStreamFile.h" |
|
27 |
+#include "Time.h" |
|
28 |
+#include "TimeCallee.h" |
|
29 |
+#include "UIntFile.h" |
|
30 |
+ |
|
31 |
+namespace Blinker { |
|
32 |
+ |
|
33 |
+/// tetris game |
|
34 |
+class Tetris: public Game, public OpReqIf |
|
35 |
+{ |
|
36 |
+protected: |
|
37 |
+ /// coordinates of a pixel part of a stone |
|
38 |
+ struct Coord { |
|
39 |
+ int x; |
|
40 |
+ int y; |
|
41 |
+ }; |
|
42 |
+ |
|
43 |
+ /// descriptor of a certain rotation of a stone |
|
44 |
+ struct RotStone { |
|
45 |
+ Coord pixels[4]; ///< coordinates of the 4 pixels |
|
46 |
+ }; |
|
47 |
+ |
|
48 |
+ /// descriptor of a stone |
|
49 |
+ struct Stone { |
|
50 |
+ RotStone rot[4]; ///< rotations of the stone |
|
51 |
+ }; |
|
52 |
+ |
|
53 |
+public: |
|
54 |
+ /** |
|
55 |
+ * @brief constructor |
|
56 |
+ * @param[in] name module name |
|
57 |
+ * @param[in] mgrs managers |
|
58 |
+ * @param[in] dirBase base directory |
|
59 |
+ */ |
|
60 |
+ Tetris(const std::string &name, Mgrs &mgrs, const Directory &dirBase); |
|
61 |
+ |
|
62 |
+ /// virtual destructor |
|
63 |
+ virtual ~Tetris(); |
|
64 |
+ |
|
65 |
+private: |
|
66 |
+ /// copy constructor disabled |
|
67 |
+ Tetris(const Tetris &that); |
|
68 |
+ |
|
69 |
+ /// assignment operator disabled |
|
70 |
+ const Tetris & operator=(const Tetris &that); |
|
71 |
+ |
|
72 |
+public: |
|
73 |
+ /// check for update of configuration (derived game), return true on update |
|
74 |
+ virtual bool updateConfigGame(); |
|
75 |
+ |
|
76 |
+ /** |
|
77 |
+ * @brief check if accepting new operator connection is possible |
|
78 |
+ * @param[in] name operator interface name |
|
79 |
+ * @return if accepting new connection is possible |
|
80 |
+ */ |
|
81 |
+ virtual bool acceptNewOpConn(const std::string &name); |
|
82 |
+ |
|
83 |
+ /** |
|
84 |
+ * @brief new operator connection |
|
85 |
+ * @param[in] name operator interface name |
|
86 |
+ * @param[in] pConn operator connection object |
|
87 |
+ * |
|
88 |
+ * The new connection may not yet be used for sending inside this callback. |
|
89 |
+ */ |
|
90 |
+ virtual void newOpConn(const std::string &name, OpConn *pConn); |
|
91 |
+ |
|
92 |
+ /** |
|
93 |
+ * @brief key command received on operator connection |
|
94 |
+ * @param[in] pConn operator connection object |
|
95 |
+ * @param[in] key key that was pressed |
|
96 |
+ */ |
|
97 |
+ virtual void opConnRecvKey(OpConn *pConn, char key); |
|
98 |
+ |
|
99 |
+ /** |
|
100 |
+ * @brief play command received on operator connection |
|
101 |
+ * @param[in] pConn operator connection object |
|
102 |
+ * @param[in] sound name of sound to play |
|
103 |
+ */ |
|
104 |
+ virtual void opConnRecvPlay(OpConn *pConn, const std::string &sound); |
|
105 |
+ |
|
106 |
+ /** |
|
107 |
+ * @brief operator connection is closed |
|
108 |
+ * @param[in] pConn operator connection object |
|
109 |
+ * |
|
110 |
+ * The connection may not be used for sending any more in this callback. |
|
111 |
+ */ |
|
112 |
+ virtual void opConnClose(OpConn *pConn); |
|
113 |
+ |
|
114 |
+protected: |
|
115 |
+ /// re-initialize game (e.g. due to config change) |
|
116 |
+ virtual void reinitialize(); |
|
117 |
+ |
|
118 |
+ /// redraw current game image, expected to call sendFrame() at end |
|
119 |
+ virtual void redraw(); |
|
120 |
+ |
|
121 |
+ /// process next time step of game |
|
122 |
+ virtual void timeStep(); |
|
123 |
+ |
|
124 |
+ /// set up a new stone |
|
125 |
+ void newStone(); |
|
126 |
+ |
|
127 |
+ /// set time for next time step of game - or unset if not needed |
|
128 |
+ void planTimeStep(); |
|
129 |
+ |
|
130 |
+ /// check if stone fits at position |
|
131 |
+ bool checkStoneFit(int stone, int rot, int y, int x) const; |
|
132 |
+ |
|
133 |
+ /// draw a stone to image buffer |
|
134 |
+ void drawStone(int stone, int rot, int y, int x); |
|
135 |
+ |
|
136 |
+ /// wipe a stone from image buffer (i.e. replace it with background color) |
|
137 |
+ void wipeStone(int stone, int rot, int y, int x); |
|
138 |
+ |
|
139 |
+ /// set shape of stone to color in image buffer |
|
140 |
+ void colorStone(int stone, int rot, int y, int x, ColorData const &color); |
|
141 |
+ |
|
142 |
+protected: |
|
143 |
+ /// stone data |
|
144 |
+ static Stone const c_stones[7]; |
|
145 |
+ |
|
146 |
+ static int const c_stoneCnt; ///< number of stones |
|
147 |
+ static int const c_rotCnt; ///< number of rotations per stone |
|
148 |
+ static int const c_pixelCnt; ///< number of pixels per stone |
|
149 |
+ |
|
150 |
+ /// descriptor for delay value |
|
151 |
+ static ValueDescr const c_delayDescr; |
|
152 |
+ |
|
153 |
+ ColorFile m_fileStoneColor; ///< color file for stone color |
|
154 |
+ UIntFile m_fileDelay; ///< file for initial delay in ms per frame |
|
155 |
+ |
|
156 |
+ NameFile m_fileStartSound; ///< "start game" sound name file |
|
157 |
+ |
|
158 |
+ ColorData m_stoneColor; ///< stone color |
|
159 |
+ unsigned int m_delay; ///< initial delay in ms per frame (ball speed) |
|
160 |
+ |
|
161 |
+ OpConn *m_pConn; ///< operator connection of player (or NULL) |
|
162 |
+ |
|
163 |
+ int m_stone; ///< index of current stone |
|
164 |
+ int m_rot; ///< rotation of current stone |
|
165 |
+ int m_posX; ///< x position of current stone |
|
166 |
+ int m_posY; ///< y position of current stone |
|
167 |
+ |
|
168 |
+ /// tetris field (y * m_width + x), -1 for free, >= 0 for pixel from stone |
|
169 |
+ std::vector<int> m_field; |
|
170 |
+}; // class Tetris |
|
171 |
+ |
|
172 |
+} // namespace Blinker |
|
173 |
+ |
|
174 |
+#endif // #ifndef BLINKER_TETRIS_H |
|
175 |
+ |
... | ... |
@@ -30,6 +30,7 @@ |
30 | 30 |
#include "SipPhone.h" |
31 | 31 |
#include "SyncNameSplitter.h" |
32 | 32 |
#include "SyncPrinter.h" |
33 |
+#include "Tetris.h" |
|
33 | 34 |
#include "Transformer.h" |
34 | 35 |
#include "Udp4Phone.h" |
35 | 36 |
#include "Udp4Receiver.h" |
... | ... |
@@ -70,6 +71,7 @@ void run(const std::string &dirConfig) |
70 | 71 |
MODULEMGR(SipPhone, sipphones); |
71 | 72 |
MODULEMGR(SyncNameSplitter, syncnamesplitters); |
72 | 73 |
MODULEMGR(SyncPrinter, syncprinters); |
74 |
+ MODULEMGR(Tetris, tetrises); |
|
73 | 75 |
MODULEMGR(Transformer, transformers); |
74 | 76 |
MODULEMGR(Udp4Phone, udp4phones); |
75 | 77 |
MODULEMGR(Udp4Receiver, udp4receivers); |
76 | 78 |