"Fly 'n' Shoot" game model from Chapters 1 & 9 of PSiCC2 This constructor is called only for statically allocated MineEvt NOTE: QP can call event constructors in Q_NEW, when the port is configured with the Q_EVT_CTOR macro sig = sig_p; poolId_ = 0U; id = id_p; This constructor is called only for statically allocated MineEvt NOTE: QP can call event constructors in Q_NEW, when the port is configured with the Q_EVT_CTOR macro sig = sig_p; poolId_ = 0U; score = score_p; Tunnel Active Object 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] = (QHsm *)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] == (QHsm *)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); 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 } } 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] != (QHsm *)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); Q_ASSERT((Q_EVT_CAST(MineEvt)->id < GAME_MINES_MAX) && (me->m_mines[Q_EVT_CAST(MineEvt)->id] != (QHsm *)0)); me->m_mines[Q_EVT_CAST(MineEvt)->id] = (QHsm *)0; 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); static QP::QEvt const takeoff = QEVT_INITIALIZER(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 QP::QEvt recycle; recycle.sig = 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 = QEVT_INITIALIZER(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 = QEVT_INITIALIZER(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); 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); // 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 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); me->m_x = Q_EVT_CAST(ObjectPosEvt)->x; me->m_y = Q_EVT_CAST(ObjectPosEvt)->y; me->m_score = 0U; /* reset the score */ ScoreEvt *sev = Q_NEW(ScoreEvt, SCORE_SIG); sev->score = me->m_score; AO_Tunnel->POST(sev, me); // tell the Tunnel to draw the Ship and test for hits ObjectImageEvt *oie = Q_NEW(ObjectImageEvt, SHIP_IMG_SIG); oie->x = me->m_x; oie->y = me->m_y; oie->bmp = SHIP_BMP; AO_Tunnel->POST(oie, me); ++me->m_score; // increment the score for surviving another tick if ((me->m_score % 10U) == 0U) { // is the score "round"? ScoreEvt *sev = Q_NEW(ScoreEvt, SCORE_SIG); sev->score = me->m_score; AO_Tunnel->POST(sev, me); } ObjectPosEvt *ope = Q_NEW(ObjectPosEvt, MISSILE_FIRE_SIG); ope->x = me->m_x; ope->y = me->m_y + SHIP_HEIGHT - 1U; AO_Missile->POST(ope, 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 ObjectImageEvt *oie = Q_NEW(ObjectImageEvt, EXPLOSION_SIG); oie->bmp = EXPLOSION0_BMP + (me->m_exp_ctr >> 2); oie->x = me->m_x; // x of explosion oie->y = (int8_t)((int)me->m_y - 4U + SHIP_HEIGHT); AO_Tunnel->POST(oie, me); ScoreEvt *gameOver = Q_NEW(ScoreEvt, GAME_OVER_SIG); gameOver->score = me->m_score; AO_Tunnel->POST(gameOver, me); Missile Active Object 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 ObjectImageEvt *oie = Q_NEW(ObjectImageEvt, MISSILE_IMG_SIG); oie->x = me->m_x; oie->y = me->m_y; oie->bmp = MISSILE_BMP; AO_Tunnel->POST(oie, 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 ObjectImageEvt *oie = Q_NEW(ObjectImageEvt, EXPLOSION_SIG); oie->x = me->m_x + 3U; // x-pos of explosion oie->y = (int8_t)((int)me->m_y - 4U); // y-pos oie->bmp = EXPLOSION0_BMP + (me->m_exp_ctr >> 2); AO_Tunnel->POST(oie, me); The Mine1 orthogonal component 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 MineEvt *mev = Q_NEW(MineEvt, MINE_DISABLED_SIG); mev->id = MINE_ID(me); AO_Tunnel->POST(mev, 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 ObjectImageEvt *oie = Q_NEW(ObjectImageEvt, EXPLOSION_SIG); oie->x = me->m_x + 1U; // x of explosion oie->y = (int8_t)((int)me->m_y - 4 + 2); // y of explosion oie->bmp = EXPLOSION0_BMP + (me->m_exp_ctr >> 2); AO_Tunnel->POST(oie, 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 ObjectImageEvt *oie = Q_NEW(ObjectImageEvt, MINE_IMG_SIG); oie->x = me->m_x; oie->y = me->m_y; oie->bmp = MINE1_BMP; AO_Tunnel->POST(oie, 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 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 MineEvt *mev = Q_NEW(MineEvt, MINE_DISABLED_SIG); mev->id = MINE_ID(me); AO_Tunnel->POST(mev, 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 ObjectImageEvt *oie = Q_NEW(ObjectImageEvt, EXPLOSION_SIG); oie->x = me->m_x + 1U; // x of explosion oie->y = (int8_t)((int)me->m_y - 4 + 2); // y of explosion oie->bmp = EXPLOSION0_BMP + (me->m_exp_ctr >> 2); AO_Tunnel->POST(oie, 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 ObjectImageEvt *oie = Q_NEW(ObjectImageEvt, MINE_IMG_SIG); oie->x = me->m_x; oie->y = me->m_y; oie->bmp = MINE2_BMP; AO_Tunnel->POST(oie, 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) }; $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) 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 }; // 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) // obtain instances of the Mines orthogonal components QP::QHsm *Mine1_getInst(uint8_t id); QP::QHsm *Mine2_getInst(uint8_t id); } // namespace GAME #endif // game_h #include "qp_port.h" #include "bsp.h" #include "game.h" namespace GAME { //Q_DEFINE_THIS_FILE // local objects ------------------------------------------------------------- $declare(AOs::Missile) static Missile l_missile; // the sole instance of the Missile active object // Public-scope objects ------------------------------------------------------ QP::QActive * const AO_Missile = &l_missile; // opaque pointer // Active object definition -------------------------------------------------- $define(AOs::Missile) } // namespace GAME #include "qp_port.h" #include "bsp.h" #include "game.h" namespace GAME { //Q_DEFINE_THIS_FILE #define SHIP_WIDTH 5U #define SHIP_HEIGHT 3U // encapsulated delcaration of the Ship active object ------------------------ $declare(AOs::Ship) // local objects ------------------------------------------------------------- static Ship l_ship; // the sole instance of the Ship active object // Public-scope objects ------------------------------------------------------ QP::QActive * const AO_Ship = &l_ship; // opaque pointer // Active object definition -------------------------------------------------- $define(AOs::Ship) } // namespace GAME #include "qp_port.h" #include "bsp.h" #include "game.h" #include <string.h> // for memmove() and memcpy() namespace GAME { Q_DEFINE_THIS_FILE // local objects ------------------------------------------------------------- $declare(AOs::Tunnel) 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::QActive * 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) } }; // Active object definition ================================================== $define(AOs::Tunnel) //............................................................................ 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 * (3*7*11*13*23); 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 #include "qp_port.h" #include "bsp.h" #include "game.h" namespace GAME { Q_DEFINE_THIS_FILE // encapsulated delcaration of the Mine1 HSM --------------------------------- $declare(AOs::Mine1) // local objects ------------------------------------------------------------- static Mine1 l_mine1[GAME_MINES_MAX]; // a pool of type-1 mines //............................................................................ QP::QHsm *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); } // Mine1 class definition ---------------------------------------------------- $define(AOs::Mine1) } // namespace GAME #include "qp_port.h" #include "bsp.h" #include "game.h" namespace GAME { Q_DEFINE_THIS_FILE // encapsulated delcaration of the Mine1 HSM --------------------------------- $declare(AOs::Mine2) // local objects ------------------------------------------------------------- static Mine2 l_mine2[GAME_MINES_MAX]; // a pool of type-2 mines //............................................................................ QP::QHsm *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); } // Mine1 class definition ---------------------------------------------------- $define(AOs::Mine2) } // namespace GAME #include "qp_port.h" #include "bsp.h" #include "game.h" namespace GAME { // Local-scope objects ------------------------------------------------------- static QP::QEvt const * l_missileQueueSto[2]; static QP::QEvt const * l_shipQueueSto[3]; static QP::QEvt const * l_tunnelQueueSto[GAME_MINES_MAX + 5]; static QF_MPOOL_EL(QP::QEvt) l_smlPoolSto[10]; static QF_MPOOL_EL(ObjectImageEvt) l_medPoolSto[2*GAME_MINES_MAX + 10]; static QP::QSubscrList l_subscrSto[MAX_PUB_SIG]; //............................................................................ extern "C" int_t main() { QP::QF::init(); // initialize the framework and the underlying RT kernel BSP_init(); // initialize the Board Support Package // initialize the event pools... QP::QF::poolInit(l_smlPoolSto, sizeof(l_smlPoolSto), sizeof(l_smlPoolSto[0])); QP::QF::poolInit(l_medPoolSto, sizeof(l_medPoolSto), sizeof(l_medPoolSto[0])); QP::QF::psInit(l_subscrSto, Q_DIM(l_subscrSto)); // init publish-subscribe // send object dictionaries for event queues... QS_OBJ_DICTIONARY(l_missileQueueSto); QS_OBJ_DICTIONARY(l_shipQueueSto); QS_OBJ_DICTIONARY(l_tunnelQueueSto); // send object dictionaries for event pools... QS_OBJ_DICTIONARY(l_smlPoolSto); QS_OBJ_DICTIONARY(l_medPoolSto); // send signal dictionaries for globally published events... QS_SIG_DICTIONARY(TIME_TICK_SIG, static_cast<void *>(0)); QS_SIG_DICTIONARY(PLAYER_TRIGGER_SIG, static_cast<void *>(0)); QS_SIG_DICTIONARY(PLAYER_QUIT_SIG, static_cast<void *>(0)); QS_SIG_DICTIONARY(GAME_OVER_SIG, static_cast<void *>(0)); // start the active objects... AO_Missile->start(1U, // priority l_missileQueueSto, Q_DIM(l_missileQueueSto),// evt queue static_cast<void *>(0), 0U); // no per-thread stack AO_Ship ->start(2U, // priority l_shipQueueSto, Q_DIM(l_shipQueueSto), // evt queue static_cast<void *>(0), 0U); // no per-thread stack AO_Tunnel ->start(3U, // priority l_tunnelQueueSto, Q_DIM(l_tunnelQueueSto), // evt queue static_cast<void *>(0), 0U); // no per-thread stack return QP::QF::run(); // run the QF application } } // namespace GAME