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 |