//$file${.::tunnel.cpp} ###################################################### // // Model: game.qm // File: ${.::tunnel.cpp} // // This code has been generated by QM 4.3.1 (https://www.state-machine.com/qm). // DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. // // This program is open source software: you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // //$endhead${.::tunnel.cpp} ################################################### #include "qpcpp.h" #include "bsp.h" #include "game.h" #include // for memmove() and memcpy() Q_DEFINE_THIS_FILE // declaration of the Tunnel AO ---------------------------------------------- //$declare${AOs::Tunnel} ##################################################### namespace GAME { //${AOs::Tunnel} ............................................................. class Tunnel : public QP::QActive { private: QP::QTimeEvt m_blinkTimeEvt; QP::QTimeEvt m_screenTimeEvt; QP::QHsm * m_mines[GAME_MINES_MAX]; QP::QHsm * m_mine1_pool[GAME_MINES_MAX]; QP::QHsm * m_mine2_pool[GAME_MINES_MAX]; uint8_t m_blink_ctr; uint8_t m_last_mine_x; uint8_t m_last_mine_y; uint8_t m_wall_thickness_top; uint8_t m_wall_thickness_bottom; uint8_t m_wall_gap; public: Tunnel(); void advance(); private: void plantMine(); void dispatchToAllMines(QP::QEvt const * e); protected: static QP::QState initial(Tunnel * const me, QP::QEvt const * const e); static QP::QState active(Tunnel * const me, QP::QEvt const * const e); static QP::QState show_logo(Tunnel * const me, QP::QEvt const * const e); static QP::QState demo(Tunnel * const me, QP::QEvt const * const e); static QP::QState playing(Tunnel * const me, QP::QEvt const * const e); static QP::QState game_over(Tunnel * const me, QP::QEvt const * const e); static QP::QState screen_saver(Tunnel * const me, QP::QEvt const * const e); static QP::QState screen_saver_hide(Tunnel * const me, QP::QEvt const * const e); static QP::QState screen_saver_show(Tunnel * const me, QP::QEvt const * const e); static QP::QState final(Tunnel * const me, QP::QEvt const * const e); }; } // namespace GAME //$enddecl${AOs::Tunnel} ##################################################### namespace GAME { // local objects ------------------------------------------------------------- static Tunnel l_tunnel; // the sole instance of the Tunnel active object } // namespace GAME // Public-scope objects ------------------------------------------------------ // Check for the minimum required QP version #if (QP_VERSION < 630U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) #error qpcpp version 6.3.0 or higher required #endif //$define${AOs::AO_Tunnel} ################################################### namespace GAME { // opaque pointer //${AOs::AO_Tunnel} .......................................................... QP::QActive * const AO_Tunnel = &l_tunnel; } // namespace GAME //$enddef${AOs::AO_Tunnel} ################################################### // Active object definition ================================================== //$define${AOs::Tunnel} ###################################################### namespace GAME { //${AOs::Tunnel} ............................................................. //${AOs::Tunnel::Tunnel} ..................................................... Tunnel::Tunnel() : QActive(Q_STATE_CAST(&Tunnel::initial)), m_blinkTimeEvt(this, BLINK_TIMEOUT_SIG, 0U), m_screenTimeEvt(this, SCREEN_TIMEOUT_SIG, 0U), m_last_mine_x(0U), m_last_mine_y(0U) { for (uint8_t n = 0U; n < GAME_MINES_MAX; ++n) { m_mine1_pool[n] = Mine1_getInst(n); // initialize mine1-type pool m_mine2_pool[n] = Mine2_getInst(n); // initialize mine2-type pool m_mines[n] = static_cast(0); // mine 'n' is unused } } //${AOs::Tunnel::advance} .................................................... void Tunnel::advance() { uint32_t rnd = (BSP_random() & 0xFFU); // reduce the top wall thickness 18.75% of the time if ((rnd < 48U) && (m_wall_thickness_top > 0U)) { --m_wall_thickness_top; } // reduce the bottom wall thickness 18.75% of the time if ((rnd > 208U) && (m_wall_thickness_bottom > 0U)) { --m_wall_thickness_bottom; } rnd = (BSP_random() & 0xFFU); // grow the bottom wall thickness 19.14% of the time if ((rnd < 49U) && ((GAME_TUNNEL_HEIGHT - m_wall_thickness_top - m_wall_thickness_bottom) > m_wall_gap)) { ++m_wall_thickness_bottom; } // grow the top wall thickness 19.14% of the time if ((rnd > 207U) && ((GAME_TUNNEL_HEIGHT - m_wall_thickness_top - m_wall_thickness_bottom) > m_wall_gap)) { ++m_wall_thickness_top; } // advance the Tunnel by 1 game step to the left // and copy the Tunnel layer to the main frame buffer // BSP_advanceWalls(m_wall_thickness_top, m_wall_thickness_bottom); } //${AOs::Tunnel::plantMine} .................................................. void Tunnel::plantMine() { uint32_t rnd = (BSP_random() & 0xFFU); if (m_last_mine_x > 0U) { --m_last_mine_x; // shift the last Mine 1 position to the left } // last mine far enough? if ((m_last_mine_x + GAME_MINES_DIST_MIN < GAME_TUNNEL_WIDTH) && (rnd < 8U)) // place the mines only 5% of the time { uint8_t n; for (n = 0U; n < Q_DIM(m_mines); ++n) { // look for disabled mines if (m_mines[n] == (QHsm *)0) { break; } } if (n < Q_DIM(m_mines)) { // a disabled Mine found? rnd = (BSP_random() & 0xFFFFU); if ((rnd & 1U) == 0U) { // choose the type of the mine m_mines[n] = m_mine1_pool[n]; } else { m_mines[n] = m_mine2_pool[n]; } // new Mine is planted by the end of the tunnel m_last_mine_x = GAME_TUNNEL_WIDTH - 8U; // choose a random y-position for the Mine in the Tunnel rnd %= (GAME_TUNNEL_HEIGHT - m_wall_thickness_top - m_wall_thickness_bottom - 4U); m_last_mine_y = (uint8_t)(m_wall_thickness_top + 2U + rnd); ObjectPosEvt ope; // event to dispatch to the Mine ope.sig = MINE_PLANT_SIG; ope.x = m_last_mine_x; ope.y = m_last_mine_y; m_mines[n]->dispatch(&ope); // direct dispatch } } } //${AOs::Tunnel::dispatchToAllMines} ......................................... void Tunnel::dispatchToAllMines(QP::QEvt const * e) { for (uint8_t n = 0U; n < GAME_MINES_MAX; ++n) { if (m_mines[n] != static_cast(0)) { // is the mine used? m_mines[n]->dispatch(e); } } } //${AOs::Tunnel::SM} ......................................................... QP::QState Tunnel::initial(Tunnel * const me, QP::QEvt const * const e) { //${AOs::Tunnel::SM::initial} for (uint8_t n = 0; n < GAME_MINES_MAX; ++n) { me->m_mine1_pool[n]->init(); // take the initial tran. for Mine1 me->m_mine2_pool[n]->init(); // take the initial tran. for Mine2 } BSP_randomSeed(1234U); // seed the pseudo-random generator me->subscribe(TIME_TICK_SIG); me->subscribe(PLAYER_TRIGGER_SIG); me->subscribe(PLAYER_QUIT_SIG); // object dictionary for Tunnel object... QS_OBJ_DICTIONARY(&l_tunnel.m_blinkTimeEvt); QS_OBJ_DICTIONARY(&l_tunnel.m_screenTimeEvt); // local signals... QS_SIG_DICTIONARY(BLINK_TIMEOUT_SIG, me); QS_SIG_DICTIONARY(SCREEN_TIMEOUT_SIG, me); QS_SIG_DICTIONARY(SHIP_IMG_SIG, me); QS_SIG_DICTIONARY(MISSILE_IMG_SIG, me); QS_SIG_DICTIONARY(MINE_IMG_SIG, me); QS_SIG_DICTIONARY(MINE_DISABLED_SIG, me); QS_SIG_DICTIONARY(EXPLOSION_SIG, me); QS_SIG_DICTIONARY(SCORE_SIG, me); (void)e; // unused parameter QS_FUN_DICTIONARY(&active); QS_FUN_DICTIONARY(&show_logo); QS_FUN_DICTIONARY(&demo); QS_FUN_DICTIONARY(&playing); QS_FUN_DICTIONARY(&game_over); QS_FUN_DICTIONARY(&screen_saver); QS_FUN_DICTIONARY(&screen_saver_hide); QS_FUN_DICTIONARY(&screen_saver_show); QS_FUN_DICTIONARY(&final); return Q_TRAN(&show_logo); } //${AOs::Tunnel::SM::active} ................................................. QP::QState Tunnel::active(Tunnel * const me, QP::QEvt const * const e) { QP::QState status_; switch (e->sig) { //${AOs::Tunnel::SM::active::MINE_DISABLED} case MINE_DISABLED_SIG: { Q_ASSERT((Q_EVT_CAST(MineEvt)->id < GAME_MINES_MAX) && (me->m_mines[Q_EVT_CAST(MineEvt)->id] != static_cast(0))); me->m_mines[Q_EVT_CAST(MineEvt)->id] = static_cast(0); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::PLAYER_QUIT} case PLAYER_QUIT_SIG: { status_ = Q_TRAN(&final); break; } default: { status_ = Q_SUPER(&top); break; } } return status_; } //${AOs::Tunnel::SM::active::show_logo} ...................................... QP::QState Tunnel::show_logo(Tunnel * const me, QP::QEvt const * const e) { QP::QState status_; switch (e->sig) { //${AOs::Tunnel::SM::active::show_logo} case Q_ENTRY_SIG: { me->m_blinkTimeEvt.armX(BSP_TICKS_PER_SEC/2U, BSP_TICKS_PER_SEC/2U); // every 1/2 sec me->m_screenTimeEvt.armX(BSP_TICKS_PER_SEC*5U, 0U); // in 5 sec me->m_blink_ctr = 0U; BSP_paintString(24U, (GAME_TUNNEL_HEIGHT / 2U) - 8U, "Quantum LeAps"); BSP_paintString(16U, (GAME_TUNNEL_HEIGHT / 2U) + 0U, "state-machine.com"); BSP_paintString(1U, GAME_TUNNEL_HEIGHT - 18U, "Fire missile: BTN0"); BSP_paintString(1U, GAME_TUNNEL_HEIGHT - 10U, "Fly ship up: BTN1"); BSP_updateScreen(); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::show_logo} case Q_EXIT_SIG: { me->m_blinkTimeEvt.disarm(); me->m_screenTimeEvt.disarm(); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::show_logo::SCREEN_TIMEOUT} case SCREEN_TIMEOUT_SIG: { status_ = Q_TRAN(&demo); break; } //${AOs::Tunnel::SM::active::show_logo::BLINK_TIMEOUT} case BLINK_TIMEOUT_SIG: { me->m_blink_ctr ^= 1U; // toggle the blink counter //${AOs::Tunnel::SM::active::show_logo::BLINK_TIMEOUT::[me->m_blink_ctr==0U]} if (me->m_blink_ctr == 0U) { BSP_paintString(24U + 8U*6U, (GAME_TUNNEL_HEIGHT / 2U) - 8U, "LeAps"); BSP_updateScreen(); status_ = Q_HANDLED(); } //${AOs::Tunnel::SM::active::show_logo::BLINK_TIMEOUT::[else]} else { BSP_paintString(24U + 8U*6U, (GAME_TUNNEL_HEIGHT / 2U) - 8U, "LeaPs"); BSP_updateScreen(); status_ = Q_HANDLED(); } break; } default: { status_ = Q_SUPER(&active); break; } } return status_; } //${AOs::Tunnel::SM::active::demo} ........................................... QP::QState Tunnel::demo(Tunnel * const me, QP::QEvt const * const e) { QP::QState status_; switch (e->sig) { //${AOs::Tunnel::SM::active::demo} case Q_ENTRY_SIG: { me->m_last_mine_x = 0U; // last mine at right edge of the tunnel me->m_last_mine_y = 0U; // set the tunnel properties... me->m_wall_thickness_top = 0U; me->m_wall_thickness_bottom = 0U; me->m_wall_gap = GAME_WALLS_GAP_Y; BSP_clearWalls(); // erase the tunnel walls me->m_blinkTimeEvt.armX(BSP_TICKS_PER_SEC/2U, BSP_TICKS_PER_SEC/2U); // every 1/2 sec me->m_screenTimeEvt.armX(BSP_TICKS_PER_SEC*20U, 0U); // in 20 sec me->m_blink_ctr = 0U; // init the blink counter status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::demo} case Q_EXIT_SIG: { me->m_blinkTimeEvt.disarm(); me->m_screenTimeEvt.disarm(); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::demo::BLINK_TIMEOUT} case BLINK_TIMEOUT_SIG: { me->m_blink_ctr ^= 1U; /* toggle the blink cunter */ status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::demo::SCREEN_TIMEOUT} case SCREEN_TIMEOUT_SIG: { status_ = Q_TRAN(&screen_saver); break; } //${AOs::Tunnel::SM::active::demo::TIME_TICK} case TIME_TICK_SIG: { me->advance(); if (me->m_blink_ctr != 0U) { // add the text into the frame buffer BSP_paintString((GAME_TUNNEL_WIDTH - 10U*6U)/2U, (GAME_TUNNEL_HEIGHT - 4U)/2U, "Press BTN0"); } BSP_updateScreen(); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::demo::PLAYER_TRIGGER} case PLAYER_TRIGGER_SIG: { status_ = Q_TRAN(&playing); break; } default: { status_ = Q_SUPER(&active); break; } } return status_; } //${AOs::Tunnel::SM::active::playing} ........................................ QP::QState Tunnel::playing(Tunnel * const me, QP::QEvt const * const e) { QP::QState status_; switch (e->sig) { //${AOs::Tunnel::SM::active::playing} case Q_ENTRY_SIG: { static QP::QEvt const takeoff = QEVT_INITIALIZER(TAKE_OFF_SIG); me->m_wall_gap = GAME_WALLS_GAP_Y; AO_Ship->POST(&takeoff, me); // post the TAKEOFF sig status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::playing} case Q_EXIT_SIG: { QP::QEvt recycle; recycle.sig = MINE_RECYCLE_SIG; me->dispatchToAllMines(&recycle); // recycle all Mines status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::playing::TIME_TICK} case TIME_TICK_SIG: { // render this frame on the display BSP_updateScreen(); me->advance(); me->plantMine(); me->dispatchToAllMines(e); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::playing::SHIP_IMG} case SHIP_IMG_SIG: { uint8_t x = Q_EVT_CAST(ObjectImageEvt)->x; int8_t y = Q_EVT_CAST(ObjectImageEvt)->y; uint8_t bmp = Q_EVT_CAST(ObjectImageEvt)->bmp; // did the Ship/Missile hit the tunnel wall? if (BSP_isWallHit(bmp, x, y)) { static QP::QEvt const hit = QEVT_INITIALIZER(HIT_WALL_SIG); AO_Ship->POST(&hit, me); } BSP_paintBitmap(x, y, bmp); me->dispatchToAllMines(e); // let Mines check for hits status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::playing::MISSILE_IMG} case MISSILE_IMG_SIG: { uint8_t x = Q_EVT_CAST(ObjectImageEvt)->x; int8_t y = Q_EVT_CAST(ObjectImageEvt)->y; uint8_t bmp = Q_EVT_CAST(ObjectImageEvt)->bmp; // did the Ship/Missile hit the tunnel wall? if (BSP_isWallHit(bmp, x, y)) { static QP::QEvt const hit = QEVT_INITIALIZER(HIT_WALL_SIG); AO_Missile->POST(&hit, me); } BSP_paintBitmap(x, y, bmp); me->dispatchToAllMines(e); // let Mines check for hits status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::playing::MINE_IMG} case MINE_IMG_SIG: { BSP_paintBitmap(Q_EVT_CAST(ObjectImageEvt)->x, Q_EVT_CAST(ObjectImageEvt)->y, Q_EVT_CAST(ObjectImageEvt)->bmp); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::playing::EXPLOSION} case EXPLOSION_SIG: { BSP_paintBitmap(Q_EVT_CAST(ObjectImageEvt)->x, Q_EVT_CAST(ObjectImageEvt)->y, Q_EVT_CAST(ObjectImageEvt)->bmp); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::playing::SCORE} case SCORE_SIG: { BSP_updateScore(Q_EVT_CAST(ScoreEvt)->score); // increase difficulty of the game: // the tunnel gets narrower as the score goes up // me->m_wall_gap = (uint8_t)(GAME_WALLS_GAP_Y - Q_EVT_CAST(ScoreEvt)->score/100U); if (me->m_wall_gap < GAME_WALLS_MIN_GAP_Y) { me->m_wall_gap = GAME_WALLS_MIN_GAP_Y; } status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::playing::GAME_OVER} case GAME_OVER_SIG: { BSP_clearWalls(); BSP_updateScore(Q_EVT_CAST(ScoreEvt)->score); BSP_updateScreen(); status_ = Q_TRAN(&game_over); break; } default: { status_ = Q_SUPER(&active); break; } } return status_; } //${AOs::Tunnel::SM::active::game_over} ...................................... QP::QState Tunnel::game_over(Tunnel * const me, QP::QEvt const * const e) { QP::QState status_; switch (e->sig) { //${AOs::Tunnel::SM::active::game_over} case Q_ENTRY_SIG: { me->m_blinkTimeEvt.armX(BSP_TICKS_PER_SEC/2U, BSP_TICKS_PER_SEC/2U); // every 1/2 sec me->m_screenTimeEvt.armX(BSP_TICKS_PER_SEC*5U, 0U); // in 5 se me->m_blink_ctr = 0U; BSP_paintString((GAME_TUNNEL_WIDTH - 6U * 9U) / 2U, (GAME_TUNNEL_HEIGHT / 2U) - 4U, "Game Over"); BSP_updateScreen(); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::game_over} case Q_EXIT_SIG: { me->m_blinkTimeEvt.disarm(); me->m_screenTimeEvt.disarm(); BSP_updateScore(0U); // update the score on the display status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::game_over::BLINK_TIMEOUT} case BLINK_TIMEOUT_SIG: { me->m_blink_ctr ^= 1U; // toggle the blink counter BSP_paintString((GAME_TUNNEL_WIDTH - 6U*9U) / 2U, (GAME_TUNNEL_HEIGHT / 2U) - 4U, ((me->m_blink_ctr == 0U) ? "Game Over" : " ")); BSP_updateScreen(); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::game_over::SCREEN_TIMEOUT} case SCREEN_TIMEOUT_SIG: { status_ = Q_TRAN(&demo); break; } default: { status_ = Q_SUPER(&active); break; } } return status_; } //${AOs::Tunnel::SM::active::screen_saver} ................................... QP::QState Tunnel::screen_saver(Tunnel * const me, QP::QEvt const * const e) { QP::QState status_; switch (e->sig) { //${AOs::Tunnel::SM::active::screen_saver::initial} case Q_INIT_SIG: { status_ = Q_TRAN(&screen_saver_hide); break; } //${AOs::Tunnel::SM::active::screen_saver::PLAYER_TRIGGER} case PLAYER_TRIGGER_SIG: { status_ = Q_TRAN(&demo); break; } default: { status_ = Q_SUPER(&active); break; } } return status_; } //${AOs::Tunnel::SM::active::screen_saver::screen_saver_hide} ................ QP::QState Tunnel::screen_saver_hide(Tunnel * const me, QP::QEvt const * const e) { QP::QState status_; switch (e->sig) { //${AOs::Tunnel::SM::active::screen_saver::screen_saver_hide} case Q_ENTRY_SIG: { BSP_displayOff(); // power down the display me->m_screenTimeEvt.armX(BSP_TICKS_PER_SEC*3U, 0U); // in 3 sec status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::screen_saver::screen_saver_hide} case Q_EXIT_SIG: { me->m_screenTimeEvt.disarm(); BSP_displayOn(); // power up the display status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::screen_saver::screen_saver_hid~::SCREEN_TIMEOUT} case SCREEN_TIMEOUT_SIG: { status_ = Q_TRAN(&screen_saver_show); break; } default: { status_ = Q_SUPER(&screen_saver); break; } } return status_; } //${AOs::Tunnel::SM::active::screen_saver::screen_saver_show} ................ QP::QState Tunnel::screen_saver_show(Tunnel * const me, QP::QEvt const * const e) { QP::QState status_; switch (e->sig) { //${AOs::Tunnel::SM::active::screen_saver::screen_saver_show} case Q_ENTRY_SIG: { BSP_clearFB(); // clear the screen frame buffer uint32_t rnd = BSP_random(); BSP_paintString((uint8_t)(rnd % (GAME_TUNNEL_WIDTH - 10U*6U)), (uint8_t) (rnd % (GAME_TUNNEL_HEIGHT - 8U)), "Press BTN0"); BSP_updateScreen(); me->m_screenTimeEvt.armX(BSP_TICKS_PER_SEC/3U, 0U); // in 1/3 sec status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::screen_saver::screen_saver_show} case Q_EXIT_SIG: { me->m_screenTimeEvt.disarm(); BSP_clearFB(); BSP_updateScreen(); status_ = Q_HANDLED(); break; } //${AOs::Tunnel::SM::active::screen_saver::screen_saver_sho~::SCREEN_TIMEOUT} case SCREEN_TIMEOUT_SIG: { status_ = Q_TRAN(&screen_saver_hide); break; } default: { status_ = Q_SUPER(&screen_saver); break; } } return status_; } //${AOs::Tunnel::SM::final} .................................................. QP::QState Tunnel::final(Tunnel * const me, QP::QEvt const * const e) { QP::QState status_; switch (e->sig) { //${AOs::Tunnel::SM::final} case Q_ENTRY_SIG: { BSP_clearFB(); BSP_updateScreen(); QP::QF::stop(); // stop QF and cleanup status_ = Q_HANDLED(); break; } default: { status_ = Q_SUPER(&top); break; } } return status_; } } // namespace GAME //$enddef${AOs::Tunnel} ######################################################