/////////////////////////////////////////////////////////////////////////////// // Name: bombs1.cpp // Purpose: Bombs game // Author: P. Foggia 1996 // Modified by: Wlodzimierz Skiba (ABX) since 2003 // Created: 1996 // Copyright: (c) 1996 P. Foggia // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// /* * implementation of the methods DrawField and OnEvent of the * class BombsCanvas */ #include "wx/wxprec.h" #ifdef __BORLANDC__ # pragma hdrstop #endif #ifndef WX_PRECOMP # include "wx/wx.h" #endif //precompiled headers #include "bombs.h" // Draws the field on the device context dc // xc1,yc1 etc. are the (inclusive) limits of the area to be drawn, // expressed in cells. void BombsCanvas::DrawField(wxDC *dc, int xc1, int yc1, int xc2, int yc2) { wxString buf; wxCoord chw, chh; dc->SetPen(*wxBLACK_PEN); int x, y; int xMax = this->GetGridSizeInPixels().GetWidth(); int yMax = this->GetGridSizeInPixels().GetHeight(); for(x=xc1; x<=xc2; x++) dc->DrawLine(x*m_cellWidth*X_UNIT, 0, x*m_cellWidth*X_UNIT, yMax); for(y=xc1; y<=yc2; y++) dc->DrawLine(0, y*m_cellHeight*Y_UNIT, xMax, y*m_cellHeight*Y_UNIT); dc->SetFont(BOMBS_FONT); for(x=xc1; x<=xc2; x++) for(y=yc1; y<=yc2; y++) { if (m_game->IsMarked(x,y)) { dc->SetPen(*wxBLACK_PEN); if (m_game->IsFocussed(x, y)) dc->SetBrush(*wxMEDIUM_GREY_BRUSH); else dc->SetBrush(*wxLIGHT_GREY_BRUSH); dc->DrawRectangle( x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT, m_cellWidth*X_UNIT+1, m_cellHeight*Y_UNIT+1); buf = wxT("M"); if (!m_game->IsHidden(x,y) && m_game->IsBomb(x,y)) dc->SetTextForeground(*wxBLUE); else dc->SetTextForeground(*wxRED); dc->SetTextBackground(*wxLIGHT_GREY); dc->GetTextExtent(buf, &chw, &chh); dc->DrawText( buf, x*m_cellWidth*X_UNIT + (m_cellWidth*X_UNIT-chw)/2, y*m_cellHeight*Y_UNIT + (m_cellHeight*Y_UNIT-chh)/2 ); if (!m_game->IsHidden(x,y) && m_game->IsBomb(x,y)) { dc->SetPen(*wxRED_PEN); dc->DrawLine(x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT, (x+1)*m_cellWidth*X_UNIT, (y+1)*m_cellHeight*Y_UNIT); dc->DrawLine(x*m_cellWidth*X_UNIT, (y+1)*m_cellHeight*Y_UNIT, (x+1)*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT); } } else if (m_game->IsHidden(x,y)) { dc->SetPen(*wxBLACK_PEN); if (m_game->IsFocussed(x, y)) dc->SetBrush(*wxMEDIUM_GREY_BRUSH); else dc->SetBrush(*wxLIGHT_GREY_BRUSH); dc->DrawRectangle( x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT, m_cellWidth*X_UNIT+1, m_cellHeight*Y_UNIT+1); } else if (m_game->IsBomb(x,y)) { dc->SetPen(*wxBLACK_PEN); dc->SetBrush(*wxRED_BRUSH); dc->DrawRectangle( x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT, m_cellWidth*X_UNIT+1, m_cellHeight*Y_UNIT+1); buf = wxT("B"); dc->SetTextForeground(*wxBLACK); dc->SetTextBackground(*wxRED); dc->GetTextExtent(buf, &chw, &chh); dc->DrawText( buf, x*m_cellWidth*X_UNIT + (m_cellWidth*X_UNIT-chw)/2, y*m_cellHeight*Y_UNIT + (m_cellHeight*Y_UNIT-chh)/2); if (m_game->IsExploded(x,y)) { dc->SetPen(*wxBLUE_PEN); dc->DrawLine(x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT, (x+1)*m_cellWidth*X_UNIT, (y+1)*m_cellHeight*Y_UNIT); dc->DrawLine(x*m_cellWidth*X_UNIT, (y+1)*m_cellHeight*Y_UNIT, (x+1)*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT); } } else // Display a digit { dc->SetPen(*wxBLACK_PEN); if (m_game->IsFocussed(x, y)) dc->SetBrush(*wxMEDIUM_GREY_BRUSH); else if (m_game->IsSelected(x,y)) dc->SetBrush(*wxWHITE_BRUSH); else dc->SetBrush(*wxYELLOW_BRUSH); dc->DrawRectangle( x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT, m_cellWidth*X_UNIT+1, m_cellHeight*Y_UNIT+1); int digit_value = m_game->Get(x,y) & BG_MASK; switch(digit_value) { case 0: buf = wxT("0"); dc->SetTextForeground(*wxGREEN); break; case 1: buf = wxT("1"); dc->SetTextForeground(*wxBLUE); break; default: buf.Printf(wxT("%d"),digit_value); dc->SetTextForeground(*wxBLACK); break; } dc->GetTextExtent(buf, &chw, &chh); dc->SetTextBackground(*wxWHITE); dc->DrawText( buf, x*m_cellWidth*X_UNIT + (m_cellWidth*X_UNIT-chw)/2, y*m_cellHeight*Y_UNIT + (m_cellHeight*Y_UNIT-chh)/2); } } dc->SetFont(wxNullFont); wxString msg; msg.Printf(wxT("%d bombs, %u marked, %d remaining cells"), m_game->GetNumBombs(), m_game->GetNumMarkedCells(), m_game->GetNumRemainingCells() ); #if wxUSE_LOG && wxUSE_STATUSBAR wxLogStatus(msg); #else this->GetParent()->SetTitle(msg); #endif } // Refreshes the field image // xc1,yc1 etc. are the (inclusive) limits of the area to be drawn, // expressed in cells. void BombsCanvas::RefreshField(int xc1, int yc1, int xc2, int yc2) { wxClientDC dc(this); DrawField(& dc, xc1, yc1, xc2, yc2); if (m_bmp) { wxMemoryDC memDC; memDC.SelectObject(*m_bmp); DrawField(&memDC, xc1, yc1, xc2, yc2); memDC.SelectObject(wxNullBitmap); } } // Called when uncovering a cell. void BombsCanvas::Uncover(int x, int y) { m_game->Unhide(x,y,true); RefreshField(x, y, x, y); const int gridWidth = m_game->GetWidth(); const int gridHeight = m_game->GetHeight(); const bool hasWon = m_game->GetNumRemainingCells() == 0; if (m_game->IsBomb(x,y) || hasWon) { wxBell(); if (hasWon) { wxMessageBox(wxT("Nice! You found all the bombs!"), wxT("wxWin Bombs"), wxOK|wxCENTRE); } else // x,y is a bomb { m_game->Explode(x, y); } for(x=0; xUnhide(x,y,false); RefreshField(0, 0, gridWidth-1, gridHeight-1); } else if (0 == (m_game->Get(x, y) & BG_MASK)) { int left = ( x > 0 ) ? x-1 : 0; int right = ( x < gridWidth - 1 ) ? x+1 : gridWidth - 1; int top = ( y > 0 ) ? y-1 : 0; int bottom = ( y < gridHeight - 1 ) ? y+1 : gridHeight - 1; int i, j; for (j=top; j<=bottom; j++) for (i=left; i<=right; i++) if ( (i != x || j != y) && m_game->IsHidden(i, j) && !m_game->IsMarked(i, j) ) { Uncover(i, j); } } } // Called when the canvas receives a mouse event. void BombsCanvas::OnMouseEvent(wxMouseEvent& event) { const int gridWidth = m_game->GetWidth(); const int gridHeight = m_game->GetHeight(); wxCoord fx, fy; event.GetPosition(&fx, &fy); int x = fx/(m_cellWidth*X_UNIT); int y = fy/(m_cellHeight*Y_UNIT); if (xIsHidden(x,y) || !m_game->GetNumRemainingCells() ) ) { // store previous and current field int prevFocusX = m_game->m_gridFocusX; int prevFocusY = m_game->m_gridFocusY; m_game->m_gridFocusX = x; m_game->m_gridFocusY = y; RefreshField(prevFocusX, prevFocusY, prevFocusX, prevFocusY); m_game->Mark(x, y); RefreshField(x, y, x, y); return; } else if (event.LeftDown() && m_game->IsHidden(x,y) && !m_game->IsMarked(x,y)) { // store previous and current field int prevGridFocusX = m_game->m_gridFocusX; int prevGridFocusY = m_game->m_gridFocusY; m_game->m_gridFocusX = x; m_game->m_gridFocusY = y; RefreshField(prevGridFocusX, prevGridFocusY, prevGridFocusX, prevGridFocusY); Uncover(x, y); return; } } } void BombsCanvas::OnChar(wxKeyEvent& event) { int keyCode = event.GetKeyCode(); int prevGridFocusX = m_game->m_gridFocusX; int prevGridFocusY = m_game->m_gridFocusY; const int gridWidth = m_game->GetWidth(); const int gridHeight = m_game->GetHeight(); switch(keyCode) { case WXK_RIGHT: m_game->m_gridFocusX++; if (m_game->m_gridFocusX >= gridWidth) m_game->m_gridFocusX = 0; break; case WXK_LEFT: m_game->m_gridFocusX--; if (m_game->m_gridFocusX<0) m_game->m_gridFocusX = gridWidth-1; break; case WXK_DOWN: m_game->m_gridFocusY++; if (m_game->m_gridFocusY >= gridHeight) m_game->m_gridFocusY = 0; break; case WXK_UP: m_game->m_gridFocusY--; if (m_game->m_gridFocusY<0) m_game->m_gridFocusY = gridHeight-1; break; case WXK_RETURN: if ( (prevGridFocusX == m_game->m_gridFocusX) && (prevGridFocusY == m_game->m_gridFocusY) && (m_game->IsHidden(m_game->m_gridFocusX, m_game->m_gridFocusY)) ) { m_game->Mark(m_game->m_gridFocusX, m_game->m_gridFocusY); if (!m_game->IsMarked(m_game->m_gridFocusX, m_game->m_gridFocusY)) { Uncover(m_game->m_gridFocusX, m_game->m_gridFocusY); } RefreshField(m_game->m_gridFocusX, m_game->m_gridFocusY, m_game->m_gridFocusX, m_game->m_gridFocusY); } break; default: event.Skip(); } if ((prevGridFocusX != m_game->m_gridFocusX) || (prevGridFocusY != m_game->m_gridFocusY)) { // cause focused field to be visible after first key hit after launching new game if( m_game->m_gridFocusX < 0 ) m_game->m_gridFocusX = 0; if( m_game->m_gridFocusY < 0 ) m_game->m_gridFocusY = 0; // refresh previous field and focused field RefreshField(prevGridFocusX, prevGridFocusY, prevGridFocusX, prevGridFocusY); RefreshField(m_game->m_gridFocusX, m_game->m_gridFocusY, m_game->m_gridFocusX, m_game->m_gridFocusY); } }