"Fly 'n' Shoot" game model from Chapters 1 & 9 of PSiCC2 for Qt with GUI Starts execution of an active object and registers the object with the framework. This function is strongly OS-dependent and must be defined in the QP port to a particular platform. The function takes six arguments: 'prio' is the priority of the active object. QP allows you to start up to 63 active objects, each one having a *unique* priority number between 1 and 63 inclusive, where higher numerical values correspond to higher priority (urgency) of the active object relative to the others. 'qSto[]' and 'qLen' arguments are the storage and size of the event queue used by this active object. 'stkSto' and 'stkSize' are the stack storage and size in bytes. Please note that a per-active object stack is used only when the underlying OS requies it. If the stack is not required, or the underlying OS allocates the stack internally, the stkSto should be NULL and/or stkSize should be 0. 'ie' is an optional initialization event that can be used to pass additional startup data to the active object. (Pass NULL if your active object does not expect the initialization event). // Example: static Table l_table; // Table active object instance static QEvt const *l_tableQueueSto[N]; // storage for Table queue static int l_tableStk[256]; // storage for the stack for Table AO . . . int main() { TableEvt ie; // initialization event for the Table AO . . . ie.philNum = n; // build the initialization event . . . l_table.start( 6, // unique priority (1..QF_MAX_ACTIVE) l_tableQueueSto, // event queue of the active object Q_DIM(l_tableQueueSto),// the depth of the event queue l_tableStk, // the stack of the acitve object (optional) sizeof(l_tableStk), // the size of the stack in bytes (optional) &ie); // initialization event (optional) . . . } Posts an event e directly to the event queue of the acitve object me using the First-In-First-Out (FIFO) policy. Direct event posting is the simplest asynchronous communication method available in QP. NOTE: Direct event posting should not be confused with direct event dispatching. In contrast to asynchronous event posting through event queues, direct event dispatching is synchronous. Direct event dispatching occurs when you call QMsm::dispatch() function. // Example: extern QActive *AO_Table; . . . pe = Q_NEW(TableEvt, HUNGRY_SIG); // dynamically allocate event pe->philNum = me->num; AO_Table->postFIFO(pe); // <== Posts an event e directly to the event queue of the acitve object me using the Last-In-First-Out (LIFO) policy. Direct event posting is the simplest asynchronous communication method available in QP. NOTE: You should be very careful with the LIFO (Last In First Out) policy, because it *reverses* the order of events in the queue. Typically, the QActive_postLIFO() operation shuould be only used for self-posting of events as reminders (see the "Reminder" state pattern) for continuing a processing. The postLIFO() operation is also used in the QActive::recall() operation. NOTE: Direct event posting should not be confused with direct event dispatching. In contrast to asynchronous event posting through event queues, direct event dispatching is synchronous. Direct event dispatching occurs when you call QMsm::dispatch() function. // Example: . . . pe = Q_NEW(TableEvt, HUNGRY_SIG); // dynamically allocate event pe->philNum = me->num; . . . me->postLIFO(pe); // <== Starts execution of an active object and registers the object with the framework. This function is strongly OS-dependent and must be defined in the QP port to a particular platform. The function takes six arguments: 'prio' is the priority of the active object. QP allows you to start up to 63 active objects, each one having a *unique* priority number between 1 and 63 inclusive, where higher numerical values correspond to higher priority (urgency) of the active object relative to the others. 'qSto[]' and 'qLen' arguments are the storage and size of the event queue used by this active object. 'stkSto' and 'stkSize' are the stack storage and size in bytes. Please note that a per-active object stack is used only when the underlying OS requies it. If the stack is not required, or the underlying OS allocates the stack internally, the stkSto should be NULL and/or stkSize should be 0. 'ie' is an optional initialization event that can be used to pass additional startup data to the active object. (Pass NULL if your active object does not expect the initialization event). // Example: static Table l_table; // Table active object instance static QEvt const *l_tableQueueSto[N]; // storage for Table queue static int l_tableStk[256]; // storage for the stack for Table AO . . . int main() { TableEvt ie; // initialization event for the Table AO . . . ie.philNum = n; // build the initialization event . . . l_table.start( 6, // unique priority (1..QF_MAX_ACTIVE) l_tableQueueSto, // event queue of the active object Q_DIM(l_tableQueueSto),// the depth of the event queue l_tableStk, // the stack of the acitve object (optional) sizeof(l_tableStk), // the size of the stack in bytes (optional) &ie); // initialization event (optional) . . . } Posts an event e directly to the event queue of the acitve object me using the First-In-First-Out (FIFO) policy. Direct event posting is the simplest asynchronous communication method available in QP. NOTE: Direct event posting should not be confused with direct event dispatching. In contrast to asynchronous event posting through event queues, direct event dispatching is synchronous. Direct event dispatching occurs when you call QMsm::dispatch() function. // Example: extern QActive *AO_Table; . . . pe = Q_NEW(TableEvt, HUNGRY_SIG); // dynamically allocate event pe->philNum = me->num; AO_Table->postFIFO(pe); // <== Posts an event e directly to the event queue of the acitve object me using the Last-In-First-Out (LIFO) policy. Direct event posting is the simplest asynchronous communication method available in QP. NOTE: You should be very careful with the LIFO (Last In First Out) policy, because it *reverses* the order of events in the queue. Typically, the QActive_postLIFO() operation shuould be only used for self-posting of events as reminders (see the "Reminder" state pattern) for continuing a processing. The postLIFO() operation is also used in the QActive::recall() operation. NOTE: Direct event posting should not be confused with direct event dispatching. In contrast to asynchronous event posting through event queues, direct event dispatching is synchronous. Direct event dispatching occurs when you call QMsm::dispatch() function. // Example: . . . pe = Q_NEW(TableEvt, HUNGRY_SIG); // dynamically allocate event pe->philNum = me->num; . . . me->postLIFO(pe); // <== Tunnel Active Object : GuiQMActive(Q_STATE_CAST(&Tunnel::initial)), m_blinkTimeEvt(BLINK_TIMEOUT_SIG), m_screenTimeEvt(SCREEN_TIMEOUT_SIG), 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<QMsm *>(0); // mine 'n' is unused } uint32_t bmp1; // bimap representing 1 column of the image uint32_t rnd = (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 = (random() & 0xFFU); // grow the top wall thickness 18.75% of the time if ((rnd < 48U) && ((GAME_SCREEN_HEIGHT - m_wall_thickness_top - m_wall_thickness_bottom) > m_minimal_gap) && ((m_last_mine_x < (GAME_SCREEN_WIDTH - 5U)) || (m_last_mine_y > (m_wall_thickness_top + 1U)))) { ++m_wall_thickness_top; } // grow the bottom wall thickness 18.75% of the time if ((rnd > 208U) && ((GAME_SCREEN_HEIGHT - m_wall_thickness_top - m_wall_thickness_bottom) > m_minimal_gap) && ((m_last_mine_x < (GAME_SCREEN_WIDTH - 5U)) || (m_last_mine_y + 1U < (GAME_SCREEN_HEIGHT - m_wall_thickness_bottom)))) { ++m_wall_thickness_bottom; } // advance the Tunnel by 1 game step to the left memmove(l_walls, l_walls + GAME_SPEED_X, (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT/8U) - GAME_SPEED_X); bmp1 = (~(~0U << m_wall_thickness_top)) | (~0U << (GAME_SCREEN_HEIGHT - m_wall_thickness_bottom)); l_walls[GAME_SCREEN_WIDTH - 1] = (uint8_t)bmp1; l_walls[GAME_SCREEN_WIDTH + GAME_SCREEN_WIDTH - 1] = (uint8_t)(bmp1 >> 8); // copy the Tunnel layer to the main frame buffer memcpy(l_frame, l_walls, (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT/8U)); uint32_t rnd = (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_SCREEN_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] == static_cast<QMsm *>(0)) { break; } } if (n < Q_DIM(m_mines)) { // a disabled Mine found? rnd = (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 in the last column of the tunnel m_last_mine_x = GAME_SCREEN_WIDTH; // choose a random y-position for the Mine in the Tunnel rnd %= (GAME_SCREEN_HEIGHT - m_wall_thickness_top - m_wall_thickness_bottom - 4U); m_last_mine_y = (uint8_t)(m_wall_thickness_top + 2U + rnd); // event to dispatch to the Mine ObjectPosEvt ope(MINE_PLANT_SIG, m_last_mine_x, m_last_mine_y); m_mines[n]->dispatch(&ope); // direct dispatch } } Q_REQUIRE(bmp < Q_DIM(l_bitmap)); uint8_t w = l_bitmap[bmp].width; if (w > GAME_SCREEN_WIDTH - x_pos) { w = GAME_SCREEN_WIDTH - x_pos; } for (uint8_t x = 0U; x < w; ++x) { uint32_t bmp1; if (y_pos >= 0) { bmp1 = (l_bitmap[bmp].bits[x] << (uint8_t)y_pos); } else { bmp1 = (l_bitmap[bmp].bits[x] >> (uint8_t)(-y_pos)); } l_frame[x_pos + x] |= (uint8_t)bmp1; l_frame[x_pos + x + GAME_SCREEN_WIDTH] |= (uint8_t)(bmp1 >> 8); } for (uint8_t n = 0U; n < GAME_MINES_MAX; ++n) { if (m_mines[n] != static_cast<QMsm *>(0)) { // is the mine used? m_mines[n]->dispatch(e); } } Q_REQUIRE(bmp < Q_DIM(l_bitmap)); uint8_t w = l_bitmap[bmp].width; if (w > GAME_SCREEN_WIDTH - x_pos) { w = GAME_SCREEN_WIDTH - x_pos; } for (uint8_t x = 0U; x < w; ++x) { uint32_t bmp1 = ((uint32_t)l_bitmap[bmp].bits[x] << y_pos); if (((l_walls[x_pos + x] & (uint8_t)bmp1) != 0U) || ((l_walls[x_pos + x + GAME_SCREEN_WIDTH] & (uint8_t)(bmp1 >> 8)) != 0)) { return true; } } return false; 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 } randomSeed(1234); // seed the pseudo-random generator me->subscribe(TIME_TICK_SIG); me->subscribe(PLAYER_TRIGGER_SIG); me->subscribe(PLAYER_QUIT_SIG); QS_OBJ_DICTIONARY(&l_tunnel); // object dictionary for Tunnel object QS_OBJ_DICTIONARY(&l_tunnel.m_blinkTimeEvt); QS_OBJ_DICTIONARY(&l_tunnel.m_screenTimeEvt); QS_FUN_DICTIONARY(&Tunnel::initial); // fun. dictionaries for Tunnel HSM QS_FUN_DICTIONARY(&Tunnel::final); QS_FUN_DICTIONARY(&Tunnel::active); QS_FUN_DICTIONARY(&Tunnel::playing); QS_FUN_DICTIONARY(&Tunnel::demo); QS_FUN_DICTIONARY(&Tunnel::game_over); QS_FUN_DICTIONARY(&Tunnel::screen_saver); QS_FUN_DICTIONARY(&Tunnel::screen_saver_hide); QS_FUN_DICTIONARY(&Tunnel::screen_saver_show); QS_SIG_DICTIONARY(BLINK_TIMEOUT_SIG, &l_tunnel); // local signals QS_SIG_DICTIONARY(SCREEN_TIMEOUT_SIG, &l_tunnel); QS_SIG_DICTIONARY(SHIP_IMG_SIG, &l_tunnel); QS_SIG_DICTIONARY(MISSILE_IMG_SIG, &l_tunnel); QS_SIG_DICTIONARY(MINE_IMG_SIG, &l_tunnel); QS_SIG_DICTIONARY(MINE_DISABLED_SIG, &l_tunnel); QS_SIG_DICTIONARY(EXPLOSION_SIG, &l_tunnel); QS_SIG_DICTIONARY(SCORE_SIG, &l_tunnel); (void)e; // unused parameter Q_ASSERT((Q_EVT_CAST(MineEvt)->id < GAME_MINES_MAX) && (me->m_mines[Q_EVT_CAST(MineEvt)->id] != static_cast<QMsm *>(0))); me->m_mines[Q_EVT_CAST(MineEvt)->id] = static_cast<QMsm *>(0); me->m_blinkTimeEvt.postEvery(me, BSP_TICKS_PER_SEC/2U); // 1/2 sec me->m_screenTimeEvt.postIn(me, BSP_TICKS_PER_SEC*5U); // 5 sec timeout me->m_blink_ctr = 0U; BSP_drawNString(0U, 0U, " Quantum LeAps "); BSP_drawNString(0U, 1U, "state-machine.co"); me->m_blinkTimeEvt.disarm(); me->m_screenTimeEvt.disarm(); me->m_blink_ctr ^= 1U; // toggle the blink couner me->m_blink_ctr == 0U BSP_drawNString(6U*9U, 0U, " LeAps"); BSP_drawNString(0U, 1U, "state-machine.co"); else BSP_drawNString(6U*9U, 0U, "LeaPs "); BSP_drawNString(0U, 1U, "tate-machine.com"); 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_minimal_gap = GAME_SCREEN_HEIGHT - 3U; // erase the tunnel walls memset(l_walls, (uint8_t)0, (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT/8U)); me->m_blinkTimeEvt.postEvery(me, BSP_TICKS_PER_SEC/2U); // 1/2 sec me->m_screenTimeEvt.postIn(me, BSP_TICKS_PER_SEC*20U); // 20 sec me->m_blink_ctr = 0U; // init the blink counter me->m_blinkTimeEvt.disarm(); me->m_screenTimeEvt.disarm(); me->m_blink_ctr ^= 1U; /* toggle the blink cunter */ me->advance(); if (me->m_blink_ctr != 0U) { // add the text bitmap into the frame buffer me->addImageAt(PRESS_BUTTON_BMP, (GAME_SCREEN_WIDTH - 55U)/2U, (GAME_SCREEN_HEIGHT - 8U)/2U); } BSP_drawBitmap(l_frame); me->m_blinkTimeEvt.postEvery(me, BSP_TICKS_PER_SEC/2U); // 1/2 sec me->m_screenTimeEvt.postIn(me, BSP_TICKS_PER_SEC*5U); // 5 sec timeout me->m_blink_ctr = 0U; BSP_drawNString((GAME_SCREEN_WIDTH - 6U*9U)/2U, 0U, "Game Over"); me->m_blinkTimeEvt.disarm(); me->m_screenTimeEvt.disarm(); BSP_updateScore(0U); // update the score on the display me->m_blink_ctr ^= 1U; // toggle the blink couner BSP_drawNString((GAME_SCREEN_WIDTH - 6U*9U)/2U, 0U, ((me->m_blink_ctr == 0U) ? "Game Over" : " ")); BSP_displayOff(); // power down the display me->m_screenTimeEvt.postIn(me, BSP_TICKS_PER_SEC*3U); // 3s timeout me->m_screenTimeEvt.disarm(); BSP_displayOn(); // power up the display uint32_t rnd = random(); // clear the screen frame buffer memset(l_frame, (uint8_t)0, (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT/8U)); me->addImageAt(PRESS_BUTTON_BMP, (uint8_t)(rnd % (GAME_SCREEN_WIDTH - 55U)), (int8_t) (rnd % (GAME_SCREEN_HEIGHT - 8U))); BSP_drawBitmap(l_frame); me->m_screenTimeEvt.postIn(me, BSP_TICKS_PER_SEC/3U); // 1/3 sec timeout me->m_screenTimeEvt.disarm(); // clear the screen frame buffer memset(l_frame, (uint8_t)0, (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT/8U)); BSP_drawBitmap(l_frame); static QP::QEvt const takeoff(TAKE_OFF_SIG); me->m_minimal_gap = GAME_SCREEN_HEIGHT - 3U; // erase the walls memset(l_walls, (uint8_t)0, (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT/8U)); AO_Ship->POST(&takeoff, me); // post the TAKEOFF sig static QP::QEvt const recycle(MINE_RECYCLE_SIG); me->dispatchToAllMines(&recycle); // recycle all Mines // render this frame on the display BSP_drawBitmap(l_frame); me->advance(); me->plantMine(); me->dispatchToAllMines(e); 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 (me->isWallHit(bmp, x, y)) { static QP::QEvt const hit(HIT_WALL_SIG); AO_Ship->POST(&hit, me); } me->addImageAt(bmp, x, y); me->dispatchToAllMines(e); // let Mines check for hits 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 (me->isWallHit(bmp, x, y)) { static QP::QEvt const hit(HIT_WALL_SIG); AO_Missile->POST(&hit, me); } me->addImageAt(bmp, x, y); me->dispatchToAllMines(e); // let Mines check for hits me->addImageAt(Q_EVT_CAST(ObjectImageEvt)->bmp, Q_EVT_CAST(ObjectImageEvt)->x, Q_EVT_CAST(ObjectImageEvt)->y); me->addImageAt(Q_EVT_CAST(ObjectImageEvt)->bmp, Q_EVT_CAST(ObjectImageEvt)->x, Q_EVT_CAST(ObjectImageEvt)->y); BSP_updateScore(Q_EVT_CAST(ScoreEvt)->score); // increase difficulty of the game: // the tunnel gets narrower as the score goes up // me->m_minimal_gap = (uint8_t)(GAME_SCREEN_HEIGHT - 3U - Q_EVT_CAST(ScoreEvt)->score/2000U); uint16_t score = Q_EVT_CAST(ScoreEvt)->score; BSP_updateScore(score); // clear the screen memset(l_frame, (uint8_t)0, (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT/8U)); BSP_drawBitmap(l_frame); // Output the final score to the screen BSP_drawNString((GAME_SCREEN_WIDTH - 6U*10U)/2U, 1U, "Score:"); char str[5]; str[4] = '\0'; // zero-terminate the string str[3] = '0' + (score % 10U); score /= 10U; str[2] = '0' + (score % 10U); score /= 10U; str[1] = '0' + (score % 10U); score /= 10U; str[0] = '0' + (score % 10U); BSP_drawNString((GAME_SCREEN_WIDTH - 6U*10U)/2U + 6U*6U, 1U, str); // clear the screen memset(l_frame, (uint8_t)0, (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT/8U)); BSP_drawBitmap(l_frame); QP::QF::stop(); /* stop QF and cleanup */ Ship Active Object : QMActive(Q_STATE_CAST(&Ship::initial)), m_x(GAME_SHIP_X), m_y(GAME_SHIP_Y) me->subscribe(TIME_TICK_SIG); me->subscribe(PLAYER_TRIGGER_SIG); // object dictionaries... QS_OBJ_DICTIONARY(&l_ship); // function dictionaries for Ship HSM... QS_FUN_DICTIONARY(&Ship::initial); QS_FUN_DICTIONARY(&Ship::active); QS_FUN_DICTIONARY(&Ship::parked); QS_FUN_DICTIONARY(&Ship::flying); QS_FUN_DICTIONARY(&Ship::exploding); // local signals... QS_SIG_DICTIONARY(PLAYER_SHIP_MOVE_SIG, &l_ship); QS_SIG_DICTIONARY(TAKE_OFF_SIG, &l_ship); QS_SIG_DICTIONARY(HIT_WALL_SIG, &l_ship); QS_SIG_DICTIONARY(HIT_MINE_SIG, &l_ship); QS_SIG_DICTIONARY(DESTROYED_MINE_SIG, &l_ship); (void)e; // unused parameter me->m_x = Q_EVT_CAST(ObjectPosEvt)->x; me->m_y = Q_EVT_CAST(ObjectPosEvt)->y; me->m_score = 0U; /* reset the score */ AO_Tunnel->POST(Q_NEW(ScoreEvt, SCORE_SIG, me->m_score), me); // tell the Tunnel to draw the Ship and test for hits AO_Tunnel->POST(Q_NEW(ObjectImageEvt, SHIP_IMG_SIG, me->m_x, me->m_y, SHIP_BMP), me); ++me->m_score; // increment the score for surviving another tick if ((me->m_score % 10U) == 0U) { // is the score "round"? AO_Tunnel->POST(Q_NEW(ScoreEvt, SCORE_SIG, me->m_score), me); } AO_Missile->POST(Q_NEW(ObjectPosEvt, MISSILE_FIRE_SIG, me->m_x, me->m_y + SHIP_HEIGHT - 1U), me); me->m_score += Q_EVT_CAST(ScoreEvt)->score; // the score will be sent to the Tunnel by the next TIME_TICK me->m_exp_ctr = 0U; me->m_exp_ctr < 15U ++me->m_exp_ctr; // tell the Tunnel to draw the current stage of Explosion AO_Tunnel->POST(Q_NEW(ObjectImageEvt, EXPLOSION_SIG, me->m_x, (int8_t)((int)me->m_y - 4U + SHIP_HEIGHT), EXPLOSION0_BMP + (me->m_exp_ctr >> 2)), me); AO_Tunnel->POST(Q_NEW(ScoreEvt, GAME_OVER_SIG, me->m_score), me); Missile Active Object : QMActive(Q_STATE_CAST(&Missile::initial)) me->subscribe( TIME_TICK_SIG); QS_OBJ_DICTIONARY(&l_missile); // object dictionary for Missile object QS_FUN_DICTIONARY(&Missile::initial); // dictionaries for Missile HSM QS_FUN_DICTIONARY(&Missile::armed); QS_FUN_DICTIONARY(&Missile::flying); QS_FUN_DICTIONARY(&Missile::exploding); QS_SIG_DICTIONARY(MISSILE_FIRE_SIG, &l_missile); // local signals QS_SIG_DICTIONARY(HIT_WALL_SIG, &l_missile); QS_SIG_DICTIONARY(DESTROYED_MINE_SIG, &l_missile); me->m_x = Q_EVT_CAST(ObjectPosEvt)->x; me->m_y = Q_EVT_CAST(ObjectPosEvt)->y; me->m_x + GAME_MISSILE_SPEED_X < GAME_SCREEN_WIDTH me->m_x += GAME_MISSILE_SPEED_X; // tell the Tunnel to draw the Missile and test for wall hits AO_Tunnel->POST(Q_NEW(ObjectImageEvt, MISSILE_IMG_SIG, me->m_x, me->m_y, MISSILE_BMP), me); AO_Ship->POST(e, me); me->m_exp_ctr = 0U; (me->m_x >= GAME_SPEED_X) && (me->m_exp_ctr < 15U) ++me->m_exp_ctr; // advance the explosion counter me->m_x -= GAME_SPEED_X; // move the explosion by one step // tell the Tunnel to render the current stage of Explosion AO_Tunnel->POST(Q_NEW(ObjectImageEvt, EXPLOSION_SIG, me->m_x + 3U, (int8_t)((int)me->m_y - 4U), EXPLOSION0_BMP + (me->m_exp_ctr >> 2)), me); The Mine1 orthogonal component : QMsm(Q_STATE_CAST(&Mine1::initial)) static bool dict_sent = false; if (!dict_sent) { dict_sent = true; QS_OBJ_DICTIONARY(&l_mine1[0]); // obj. dictionaries for Mine1 pool QS_OBJ_DICTIONARY(&l_mine1[1]); QS_OBJ_DICTIONARY(&l_mine1[2]); QS_OBJ_DICTIONARY(&l_mine1[3]); QS_OBJ_DICTIONARY(&l_mine1[4]); QS_FUN_DICTIONARY(&Mine1::initial);// fun. dictionaries for Mine1 HSM QS_FUN_DICTIONARY(&Mine1::unused); QS_FUN_DICTIONARY(&Mine1::used); QS_FUN_DICTIONARY(&Mine1::planted); QS_FUN_DICTIONARY(&Mine1::exploding); } // local signals QS_SIG_DICTIONARY(MINE_PLANT_SIG, me); QS_SIG_DICTIONARY(MINE_DISABLED_SIG, me); QS_SIG_DICTIONARY(MINE_RECYCLE_SIG, me); QS_SIG_DICTIONARY(SHIP_IMG_SIG, me); QS_SIG_DICTIONARY(MISSILE_IMG_SIG, me); me->m_x = Q_EVT_CAST(ObjectPosEvt)->x; me->m_y = Q_EVT_CAST(ObjectPosEvt)->y; // tell the Tunnel that this mine is becoming disabled AO_Tunnel->POST(Q_NEW(MineEvt, MINE_DISABLED_SIG, MINE_ID(me)), me); me->m_exp_ctr = 0U; (me->m_x >= GAME_SPEED_X) && (me->m_exp_ctr < 15) ++me->m_exp_ctr; // advance the explosion counter me->m_x -= GAME_SPEED_X; // move explosion by 1 step // tell the Game to render the current stage of Explosion AO_Tunnel->POST(Q_NEW(ObjectImageEvt, EXPLOSION_SIG, me->m_x + 1U, (int8_t)((int)me->m_y - 4 + 2), EXPLOSION0_BMP + (me->m_exp_ctr >> 2)), me); me->m_x >= GAME_SPEED_X me->m_x -= GAME_SPEED_X; // move the mine 1 step // tell the Tunnel to draw the Mine AO_Tunnel->POST(Q_NEW(ObjectImageEvt, MINE_IMG_SIG, me->m_x, me->m_y, MINE1_BMP), me); uint8_t x = Q_EVT_CAST(ObjectImageEvt)->x; uint8_t y = Q_EVT_CAST(ObjectImageEvt)->y; uint8_t bmp = Q_EVT_CAST(ObjectImageEvt)->bmp; do_bitmaps_overlap(MINE1_BMP, me->m_x, me->m_y, bmp, x, y) static MineEvt const mine1_hit(HIT_MINE_SIG, 1U); AO_Ship->POST(&mine1_hit, me); // go straight to 'disabled' and let the Ship do // the exploding uint8_t x = Q_EVT_CAST(ObjectImageEvt)->x; uint8_t y = Q_EVT_CAST(ObjectImageEvt)->y; uint8_t bmp = Q_EVT_CAST(ObjectImageEvt)->bmp; do_bitmaps_overlap(MINE1_BMP, me->m_x, me->m_y, bmp, x, y) static ScoreEvt const mine1_destroyed(DESTROYED_MINE_SIG, 25U); AO_Missile->POST(&mine1_destroyed, me); The Mine2 orthogonal component : QMsm(Q_STATE_CAST(&Mine2::initial)) static bool dict_sent = false; if (!dict_sent) { dict_sent = true; QS_OBJ_DICTIONARY(&l_mine2[0]); // obj. dictionaries for Mine2 pool QS_OBJ_DICTIONARY(&l_mine2[1]); QS_OBJ_DICTIONARY(&l_mine2[2]); QS_OBJ_DICTIONARY(&l_mine2[3]); QS_OBJ_DICTIONARY(&l_mine2[4]); QS_FUN_DICTIONARY(&Mine2::initial);// fun. dictionaries for Mine2 HSM QS_FUN_DICTIONARY(&Mine2::unused); QS_FUN_DICTIONARY(&Mine2::used); QS_FUN_DICTIONARY(&Mine2::planted); QS_FUN_DICTIONARY(&Mine2::exploding); } // local signals QS_SIG_DICTIONARY(MINE_PLANT_SIG, me); QS_SIG_DICTIONARY(MINE_DISABLED_SIG, me); QS_SIG_DICTIONARY(MINE_RECYCLE_SIG, me); QS_SIG_DICTIONARY(SHIP_IMG_SIG, me); QS_SIG_DICTIONARY(MISSILE_IMG_SIG, me); me->m_x = Q_EVT_CAST(ObjectPosEvt)->x; me->m_y = Q_EVT_CAST(ObjectPosEvt)->y; // tell the Tunnel that this mine is becoming disabled AO_Tunnel->POST(Q_NEW(MineEvt, MINE_DISABLED_SIG, MINE_ID(me)), me); me->m_exp_ctr = 0U; (me->m_x >= GAME_SPEED_X) && (me->m_exp_ctr < 15) ++me->m_exp_ctr; // advance the explosion counter me->m_x -= GAME_SPEED_X; // move explosion by 1 step // tell the Game to render the current stage of Explosion AO_Tunnel->POST(Q_NEW(ObjectImageEvt, EXPLOSION_SIG, me->m_x + 1U, (int8_t)((int)me->m_y - 4 + 2), EXPLOSION0_BMP + (me->m_exp_ctr >> 2)), me); me->m_x >= GAME_SPEED_X me->m_x -= GAME_SPEED_X; // move the mine 1 step // tell the Tunnel to draw the Mine AO_Tunnel->POST(Q_NEW(ObjectImageEvt, MINE_IMG_SIG, me->m_x, me->m_y, MINE2_BMP), me); uint8_t x = Q_EVT_CAST(ObjectImageEvt)->x; uint8_t y = Q_EVT_CAST(ObjectImageEvt)->y; uint8_t bmp = Q_EVT_CAST(ObjectImageEvt)->bmp; do_bitmaps_overlap(MINE2_BMP, me->m_x, me->m_y, bmp, x, y) static MineEvt const mine2_hit(HIT_MINE_SIG, 2U); AO_Ship->POST(&mine2_hit, me); // go straight to 'disabled' and let the Ship do // the exploding uint8_t x = Q_EVT_CAST(ObjectImageEvt)->x; uint8_t y = Q_EVT_CAST(ObjectImageEvt)->y; uint8_t bmp = Q_EVT_CAST(ObjectImageEvt)->bmp; do_bitmaps_overlap(MINE2_MISSILE_BMP, me->m_x, me->m_y, bmp, x, y) static ScoreEvt const mine2_destroyed(DESTROYED_MINE_SIG, 45U); AO_Missile->POST(&mine2_destroyed, me); #ifndef game_h #define game_h namespace GAME { enum GameSignals { // signals used in the game TIME_TICK_SIG = QP::Q_USER_SIG, // published from tick ISR PLAYER_TRIGGER_SIG, // published by Player (ISR) to trigger the Missile PLAYER_QUIT_SIG, // published by Player (ISR) to quit the game GAME_OVER_SIG, // published by Ship when it finishes exploding // insert other published signals here ... MAX_PUB_SIG, // the last published signal PLAYER_SHIP_MOVE_SIG, // posted by Player (ISR) to the Ship to move it BLINK_TIMEOUT_SIG, // signal for Tunnel's blink timeout event SCREEN_TIMEOUT_SIG, // signal for Tunnel's screen timeout event TAKE_OFF_SIG, // from Tunnel to Ship to grant permission to take off HIT_WALL_SIG, // from Tunnel to Ship when Ship hits the wall HIT_MINE_SIG, // from Mine to Ship or Missile when it hits the mine SHIP_IMG_SIG, // from Ship to the Tunnel to draw and check for hits MISSILE_IMG_SIG, // from Missile the Tunnel to draw and check for hits MINE_IMG_SIG, // sent by Mine to the Tunnel to draw the mine MISSILE_FIRE_SIG, // sent by Ship to the Missile to fire DESTROYED_MINE_SIG, // from Missile to Ship when Missile destroyed Mine EXPLOSION_SIG, // from any exploding object to render the explosion MINE_PLANT_SIG, // from Tunnel to the Mine to plant it MINE_DISABLED_SIG, // from Mine to Tunnel when it becomes disabled MINE_RECYCLE_SIG, // sent by Tunnel to Mine to recycle the mine SCORE_SIG, // from Ship to Tunnel to adjust game level based on score MAX_SIG // the last signal (keep always last) }; enum GameBitmapIds { PRESS_BUTTON_BMP, SHIP_BMP, MISSILE_BMP, MINE1_BMP, MINE2_BMP, MINE2_MISSILE_BMP, EXPLOSION0_BMP, EXPLOSION1_BMP, EXPLOSION2_BMP, EXPLOSION3_BMP, MAX_BMP }; // obtain instances of the Mines orthogonal components QP::QMsm *Mine1_getInst(uint8_t id); QP::QMsm *Mine2_getInst(uint8_t id); } // namespace GAME $declare(Events::ObjectPosEvt) $declare(Events::ObjectImageEvt) $declare(Events::MineEvt) $declare(Events::ScoreEvt) #define GAME_SCREEN_WIDTH BSP_SCREEN_WIDTH #define GAME_SCREEN_HEIGHT BSP_SCREEN_HEIGHT #define GAME_MINES_MAX 5U #define GAME_MINES_DIST_MIN 10U #define GAME_SPEED_X 1U #define GAME_MISSILE_SPEED_X 2U #define GAME_SHIP_X 10U #define GAME_SHIP_Y (GAME_SCREEN_HEIGHT / 2U) // opaque pointers to active objects in the application $declare(AOs::AO_Tunnel) $declare(AOs::AO_Ship) $declare(AOs::AO_Missile) // helper function for all AOs $declare(AOs::do_bitmaps_overlap) #endif // game_h #include "qp_port.h" #include "bsp.h" #include "game.h" //Q_DEFINE_THIS_FILE // local objects ------------------------------------------------------------- $declare(AOs::Missile) namespace GAME { static Missile l_missile; // the sole instance of the Missile active object // Public-scope objects ------------------------------------------------------ QP::QMActive * const AO_Missile = &l_missile; // opaque pointer } // namespace GAME // Active object definition -------------------------------------------------- $define(AOs::Missile) #include "qp_port.h" #include "bsp.h" #include "game.h" //Q_DEFINE_THIS_FILE #define SHIP_WIDTH 5U #define SHIP_HEIGHT 3U // encapsulated delcaration of the Ship active object ------------------------ $declare(AOs::Ship) namespace GAME { // local objects ------------------------------------------------------------- static Ship l_ship; // the sole instance of the Ship active object // Public-scope objects ------------------------------------------------------ QP::QMActive * const AO_Ship = &l_ship; // opaque pointer } // namespace GAME // Active object definition -------------------------------------------------- $define(AOs::Ship) #include "qp_port.h" #include "bsp.h" #include "game.h" #include <string.h> // for memmove() and memcpy() Q_DEFINE_THIS_FILE // local objects ------------------------------------------------------------- $declare(AOs::Tunnel) namespace GAME { static Tunnel l_tunnel; // the sole instance of the Tunnel active object static uint32_t l_rnd; // random seed static uint8_t l_walls[GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT/8U]; static uint8_t l_frame[GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT/8U]; // local helper functions ---------------------------------------------------- static void randomSeed(uint32_t seed); static uint32_t random(void); // Public-scope objects ------------------------------------------------------ QP::QMActive * const AO_Tunnel = &l_tunnel; // opaque pointer // helper functions ---------------------------------------------------------- // // The bitmap for the "Press Button" text: // // xxx.........................xxx........x...x........... // x..x........................x..x.......x...x........... // x..x.x.xx..xx...xxx..xxx....x..x.x..x.xxx.xxx..xx..xxx. // xxx..xx...x..x.x....x.......xxx..x..x..x...x..x..x.x..x // x....x....xxxx..xx...xx.....x..x.x..x..x...x..x..x.x..x // x....x....x.......x....x....x..x.x..x..x...x..x..x.x..x // x....x.....xxx.xxx..xxx.....xxx...xxx...x...x..xx..x..x // ....................................................... /// static uint8_t const press_button_bits[] = { 0x7F, 0x09, 0x09, 0x06, 0x00, 0x7C, 0x08, 0x04, 0x04, 0x00, 0x38, 0x54, 0x54, 0x58, 0x00, 0x48, 0x54, 0x54, 0x24, 0x00, 0x48, 0x54, 0x54, 0x24, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x49, 0x49, 0x36, 0x00, 0x3C, 0x40, 0x40, 0x7C, 0x00, 0x04, 0x3F, 0x44, 0x00, 0x04, 0x3F, 0x44, 0x00, 0x38, 0x44, 0x44, 0x38, 0x00, 0x7C, 0x04, 0x04, 0x78 }; // bitmap of the Ship: // // x.... // xxx.. // xxxxx /// static uint8_t const ship_bits[] = { 0x07, 0x06, 0x06, 0x04, 0x04 }; // bitmap of the Missile: // // xxx /// static uint8_t const missile_bits[] = { 0x01, 0x01, 0x01 }; // bitmap of the Mine type-1: // // .x. // xxx // .x. /// static uint8_t const mine1_bits[] = { 0x02, 0x07, 0x02 }; // bitmap of the Mine type-2: // // x..x // .xx. // .xx. // x..x /// static uint8_t const mine2_bits[] = { 0x09, 0x06, 0x06, 0x09 }; // Mine type-2 is nastier than Mine type-1. The type-2 mine can // hit the Ship with any of its "tentacles". However, it can be // destroyed by the Missile only by hitting its center, defined as // the following bitmap: // // .... // .xx. // .xx. // .... /// static uint8_t const mine2_missile_bits[] = { 0x00, 0x06, 0x06, 0x00 }; // The bitmap of the explosion stage 0: // // ....... // ....... // ...x... // ..x.x.. // ...x... // ....... // ....... /// static uint8_t const explosion0_bits[] = { 0x00, 0x00, 0x08, 0x14, 0x08, 0x00, 0x00 }; // The bitmap of the explosion stage 1: // // ....... // ....... // ..x.x.. // ...x... // ..x.x.. // ....... // ....... /// static uint8_t const explosion1_bits[] = { 0x00, 0x00, 0x14, 0x08, 0x14, 0x00, 0x00 }; // The bitmap of the explosion stage 2: // // ....... // .x...x. // ..x.x.. // ...x... // ..x.x.. // .x...x. // ....... /// static uint8_t const explosion2_bits[] = { 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00 }; // The bitmap of the explosion stage 3: // // x..x..x // .x.x.x. // ..x.x.. // xx.x.xx // ..x.x.. // .x.x.x. // x..x..x /// static uint8_t const explosion3_bits[] = { 0x49, 0x2A, 0x14, 0x6B, 0x14, 0x2A, 0x49 }; struct Bitmap { // the auxiliary structure to hold const bitmaps uint8_t const *bits; // the bits in the bitmap uint8_t width; // the width of the bitmap }; static Bitmap const l_bitmap[MAX_BMP] = { { press_button_bits, Q_DIM(press_button_bits) }, { ship_bits, Q_DIM(ship_bits) }, { missile_bits, Q_DIM(missile_bits) }, { mine1_bits, Q_DIM(mine1_bits) }, { mine2_bits, Q_DIM(mine2_bits) }, { mine2_missile_bits, Q_DIM(mine2_missile_bits) }, { explosion0_bits, Q_DIM(explosion0_bits) }, { explosion1_bits, Q_DIM(explosion1_bits) }, { explosion2_bits, Q_DIM(explosion2_bits) }, { explosion3_bits, Q_DIM(explosion3_bits) } }; //............................................................................ uint32_t random(void) { // a very cheap pseudo-random-number generator // "Super-Duper" Linear Congruential Generator (LCG) // LCG(2^32, 3*7*11*13*23, 0, seed) // l_rnd = l_rnd * (3U*7U*11U*13U*23U); return l_rnd >> 8; } //............................................................................ void randomSeed(uint32_t seed) { l_rnd = seed; } //............................................................................ bool do_bitmaps_overlap(uint8_t bmp_id1, uint8_t x1, uint8_t y1, uint8_t bmp_id2, uint8_t x2, uint8_t y2) { uint8_t x; uint8_t x0; uint8_t w; uint32_t bits1; uint32_t bits2; Bitmap const *bmp1; Bitmap const *bmp2; Q_REQUIRE((bmp_id1 < Q_DIM(l_bitmap)) && (bmp_id2 < Q_DIM(l_bitmap))); bmp1 = &l_bitmap[bmp_id1]; bmp2 = &l_bitmap[bmp_id2]; // is the incoming object starting to overlap the Mine bitmap? if ((x1 <= x2) && (x1 + bmp2->width > x2)) { x0 = x2 - x1; w = x1 + bmp2->width - x2; if (w > bmp1->width) { w = bmp1->width; } for (x = 0; x < w; ++x) { // scan over the overlapping columns bits1 = ((uint32_t)bmp2->bits[x + x0] << y2); bits2 = ((uint32_t)bmp1->bits[x] << y1); if ((bits1 & bits2) != 0) { // do the bits overlap? return true; // yes! } } } else { if ((x1 > x2) && (x2 + bmp1->width > x1)) { x0 = x1 - x2; w = x2 + bmp1->width - x1; if (w > bmp2->width) { w = bmp2->width; } for (x = 0; x < w; ++x) { // scan over the overlapping columns bits1 = ((uint32_t)bmp1->bits[x + x0] << y1); bits2 = ((uint32_t)bmp2->bits[x] << y2); if ((bits1 & bits2) != 0U) { // do the bits overlap? return true; // yes! } } } } return false; // the bitmaps do not overlap } } // namespace GAME // Active object definition ================================================== $define(AOs::Tunnel) #include "qp_port.h" #include "bsp.h" #include "game.h" Q_DEFINE_THIS_FILE // encapsulated delcaration of the Mine1 HSM --------------------------------- $declare(AOs::Mine1) namespace GAME { // local objects ------------------------------------------------------------- static Mine1 l_mine1[GAME_MINES_MAX]; // a pool of type-1 mines //............................................................................ QP::QMsm *Mine1_getInst(uint8_t id) { Q_REQUIRE(id < GAME_MINES_MAX); return &l_mine1[id]; } // helper function to provide the ID of this mine ............................ static inline uint8_t MINE_ID(Mine1 const * const me) { return static_cast<uint8_t>(me - l_mine1); } } // namespace GAME // Mine1 class definition ---------------------------------------------------- $define(AOs::Mine1) #include "qp_port.h" #include "bsp.h" #include "game.h" Q_DEFINE_THIS_FILE // encapsulated delcaration of the Mine1 HSM --------------------------------- $declare(AOs::Mine2) namespace GAME { // local objects ------------------------------------------------------------- static Mine2 l_mine2[GAME_MINES_MAX]; // a pool of type-2 mines //............................................................................ QP::QMsm *Mine2_getInst(uint8_t id) { Q_REQUIRE(id < GAME_MINES_MAX); return &l_mine2[id]; } // helper function to provide the ID of this mine ............................ static inline uint8_t MINE_ID(Mine2 const * const me) { return static_cast<uint8_t>(me - l_mine2); } } // namespace GAME // Mine1 class definition ---------------------------------------------------- $define(AOs::Mine2)