1///////////////////////////////////////////////////////////////////////////////
2// Name:        bombs1.cpp
3// Purpose:     Bombs game
4// Author:      P. Foggia 1996
5// Modified by: Wlodzimierz Skiba (ABX) since 2003
6// Created:     1996
7// RCS-ID:      $Id: bombs1.cpp 35650 2005-09-23 12:56:45Z MR $
8// Copyright:   (c) 1996 P. Foggia
9// Licence:     wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12/*
13 * implementation of the methods DrawField and OnEvent of the
14 * class BombsCanvas
15 */
16
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
20#   pragma hdrstop
21#endif
22
23#ifndef  WX_PRECOMP
24#   include "wx/wx.h"
25#endif //precompiled headers
26
27#include "bombs.h"
28
29// Draws the field on the device context dc
30// xc1,yc1 etc. are the (inclusive) limits of the area to be drawn,
31// expressed in cells.
32void BombsCanvas::DrawField(wxDC *dc, int xc1, int yc1, int xc2, int yc2)
33{
34    wxString buf;
35    long chw, chh;
36
37    wxColour wxYellow  = wxTheColourDatabase->Find(wxT("YELLOW"));
38    wxColour wxFocused = wxTheColourDatabase->Find(wxT("GREY"));
39
40    wxPen *bluePen = wxThePenList->FindOrCreatePen(*wxBLUE, 1, wxSOLID);
41
42    wxBrush *focusedBrush = wxTheBrushList->FindOrCreateBrush(wxFocused, wxSOLID);
43    wxBrush *yellowBrush  = wxTheBrushList->FindOrCreateBrush(wxYellow, wxSOLID);
44
45    dc->SetPen(*wxBLACK_PEN);
46
47    int x, y;
48    int xMax = this->GetGridSizeInPixels().GetWidth();
49    int yMax = this->GetGridSizeInPixels().GetHeight();
50    for(x=xc1; x<=xc2; x++)
51        dc->DrawLine(x*m_cellWidth*X_UNIT, 0, x*m_cellWidth*X_UNIT, yMax);
52    for(y=xc1; y<=yc2; y++)
53        dc->DrawLine(0, y*m_cellHeight*Y_UNIT, xMax, y*m_cellHeight*Y_UNIT);
54
55
56    wxFont font= BOMBS_FONT;
57    dc->SetFont(font);
58
59    for(x=xc1; x<=xc2; x++)
60        for(y=yc1; y<=yc2; y++)
61        {
62            if (m_game->IsMarked(x,y))
63            {
64                dc->SetPen(*wxBLACK_PEN);
65
66                if (m_game->IsFocussed(x, y))
67                    dc->SetBrush(*focusedBrush);
68                else
69                    dc->SetBrush(*wxLIGHT_GREY_BRUSH);
70
71                dc->DrawRectangle( x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT,
72                    m_cellWidth*X_UNIT+1, m_cellHeight*Y_UNIT+1);
73                buf = wxT("M");
74                if (!m_game->IsHidden(x,y) && m_game->IsBomb(x,y))
75                    dc->SetTextForeground(*wxBLUE);
76                else
77                    dc->SetTextForeground(*wxRED);
78
79                dc->SetTextBackground(*wxLIGHT_GREY);
80                dc->GetTextExtent(buf, &chw, &chh);
81                dc->DrawText( buf,
82                    x*m_cellWidth*X_UNIT + (m_cellWidth*X_UNIT-chw)/2,
83                    y*m_cellHeight*Y_UNIT + (m_cellHeight*Y_UNIT-chh)/2 );
84
85                if (!m_game->IsHidden(x,y) && m_game->IsBomb(x,y))
86                {
87                    dc->SetPen(*wxRED_PEN);
88                    dc->DrawLine(x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT,
89                        (x+1)*m_cellWidth*X_UNIT, (y+1)*m_cellHeight*Y_UNIT);
90                    dc->DrawLine(x*m_cellWidth*X_UNIT, (y+1)*m_cellHeight*Y_UNIT,
91                        (x+1)*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT);
92                }
93            }
94            else if (m_game->IsHidden(x,y))
95            {
96                dc->SetPen(*wxBLACK_PEN);
97                if (m_game->IsFocussed(x, y))
98                    dc->SetBrush(*focusedBrush);
99                else
100                    dc->SetBrush(*wxLIGHT_GREY_BRUSH);
101
102                dc->DrawRectangle( x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT,
103                    m_cellWidth*X_UNIT+1, m_cellHeight*Y_UNIT+1);
104            }
105            else if (m_game->IsBomb(x,y))
106            {
107                dc->SetPen(*wxBLACK_PEN);
108                dc->SetBrush(*wxRED_BRUSH);
109                dc->DrawRectangle( x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT,
110                    m_cellWidth*X_UNIT+1, m_cellHeight*Y_UNIT+1);
111                buf = wxT("B");
112                dc->SetTextForeground(*wxBLACK);
113                dc->SetTextBackground(*wxRED);
114                dc->GetTextExtent(buf, &chw, &chh);
115                dc->DrawText( buf,
116                    x*m_cellWidth*X_UNIT + (m_cellWidth*X_UNIT-chw)/2,
117                    y*m_cellHeight*Y_UNIT + (m_cellHeight*Y_UNIT-chh)/2);
118                if (m_game->IsExploded(x,y))
119                {
120                    dc->SetPen(*bluePen);
121                    dc->DrawLine(x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT,
122                        (x+1)*m_cellWidth*X_UNIT, (y+1)*m_cellHeight*Y_UNIT);
123                    dc->DrawLine(x*m_cellWidth*X_UNIT, (y+1)*m_cellHeight*Y_UNIT,
124                        (x+1)*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT);
125                }
126            }
127            else   // Display a digit
128            {
129                dc->SetPen(*wxBLACK_PEN);
130                if (m_game->IsFocussed(x, y))
131                    dc->SetBrush(*focusedBrush);
132                else if (m_game->IsSelected(x,y))
133                    dc->SetBrush(*wxWHITE_BRUSH);
134                else
135                    dc->SetBrush(*yellowBrush);
136                dc->DrawRectangle( x*m_cellWidth*X_UNIT, y*m_cellHeight*Y_UNIT,
137                    m_cellWidth*X_UNIT+1, m_cellHeight*Y_UNIT+1);
138
139                int digit_value = m_game->Get(x,y) & BG_MASK;
140                switch(digit_value)
141                {
142                case 0:
143                    buf = wxT("0");
144                    dc->SetTextForeground(*wxGREEN);
145                    break;
146                case 1:
147                    buf = wxT("1");
148                    dc->SetTextForeground(*wxBLUE);
149                    break;
150                default:
151                    buf.Printf(wxT("%d"),digit_value);
152                    dc->SetTextForeground(*wxBLACK);
153                    break;
154            }
155            dc->GetTextExtent(buf, &chw, &chh);
156            dc->SetTextBackground(*wxWHITE);
157            dc->DrawText( buf,
158                x*m_cellWidth*X_UNIT + (m_cellWidth*X_UNIT-chw)/2,
159                y*m_cellHeight*Y_UNIT + (m_cellHeight*Y_UNIT-chh)/2);
160        }
161    }
162    dc->SetFont(wxNullFont);
163
164    wxString msg;
165    msg.Printf(wxT("%d bombs, %u marked, %d remaining cells"),
166               m_game->GetNumBombs(), m_game->GetNumMarkedCells(),
167               m_game->GetNumRemainingCells() );
168
169#if wxUSE_LOG && wxUSE_STATUSBAR
170    wxLogStatus(msg);
171#else
172    this->GetParent()->SetTitle(msg);
173#endif
174}
175
176// Refreshes the field image
177// xc1,yc1 etc. are the (inclusive) limits of the area to be drawn,
178// expressed in cells.
179void BombsCanvas::RefreshField(int xc1, int yc1, int xc2, int yc2)
180{
181    wxClientDC dc(this);
182    DrawField(& dc, xc1, yc1, xc2, yc2);
183    if (m_bmp)
184    {
185        wxMemoryDC memDC;
186        memDC.SelectObject(*m_bmp);
187        DrawField(&memDC, xc1, yc1, xc2, yc2);
188        memDC.SelectObject(wxNullBitmap);
189    }
190}
191
192// Called when uncovering a cell.
193void BombsCanvas::Uncover(int x, int y)
194{
195    m_game->Unhide(x,y,true);
196    RefreshField(x, y, x, y);
197
198    const int gridWidth = m_game->GetWidth();
199    const int gridHeight = m_game->GetHeight();
200
201    const bool hasWon = m_game->GetNumRemainingCells() == 0;
202    if (m_game->IsBomb(x,y) || hasWon)
203    {
204        wxBell();
205        if (hasWon)
206        {
207            wxMessageBox(wxT("Nice! You found all the bombs!"),
208                wxT("wxWin Bombs"), wxOK|wxCENTRE);
209        }
210        else // x,y is a bomb
211        {
212            m_game->Explode(x, y);
213        }
214
215        for(x=0; x<gridWidth; x++)
216            for(y=0; y<gridHeight; y++)
217                m_game->Unhide(x,y,false);
218
219        RefreshField(0, 0, gridWidth-1, gridHeight-1);
220    }
221    else if (0 == (m_game->Get(x, y) & BG_MASK))
222    {
223        int left = ( x > 0 ) ? x-1 : 0;
224        int right = ( x < gridWidth - 1 )
225            ? x+1
226            : gridWidth - 1;
227        int top = ( y > 0 ) ? y-1 : 0;
228        int bottom = ( y < gridHeight - 1 )
229            ? y+1
230            : gridHeight - 1;
231
232        int i, j;
233        for (j=top; j<=bottom; j++)
234            for (i=left; i<=right; i++)
235                if ( (i != x || j != y) && m_game->IsHidden(i, j)
236                    && !m_game->IsMarked(i, j) )
237                {
238                    Uncover(i, j);
239                }
240    }
241}
242
243// Called when the canvas receives a mouse event.
244void BombsCanvas::OnMouseEvent(wxMouseEvent& event)
245{
246    const int gridWidth = m_game->GetWidth();
247    const int gridHeight = m_game->GetHeight();
248
249    wxCoord fx, fy;
250    event.GetPosition(&fx, &fy);
251    int x = fx/(m_cellWidth*X_UNIT);
252    int y = fy/(m_cellHeight*Y_UNIT);
253    if (x<gridWidth && y<gridHeight)
254    {
255        if ( (event.RightDown() || (event.LeftDown() && event.ShiftDown()))
256           && (m_game->IsHidden(x,y)
257               || !m_game->GetNumRemainingCells() ) )
258        {
259            // store previous and current field
260            int prevFocusX = m_game->m_gridFocusX;
261            int prevFocusY = m_game->m_gridFocusY;
262            m_game->m_gridFocusX = x;
263            m_game->m_gridFocusY = y;
264            RefreshField(prevFocusX, prevFocusY, prevFocusX, prevFocusY);
265            m_game->Mark(x, y);
266            RefreshField(x, y, x, y);
267            return;
268        }
269        else if (event.LeftDown() && m_game->IsHidden(x,y)
270            && !m_game->IsMarked(x,y))
271        {
272            // store previous and current field
273            int prevGridFocusX = m_game->m_gridFocusX;
274            int prevGridFocusY = m_game->m_gridFocusY;
275            m_game->m_gridFocusX = x;
276            m_game->m_gridFocusY = y;
277            RefreshField(prevGridFocusX, prevGridFocusY,
278                prevGridFocusX, prevGridFocusY);
279            Uncover(x, y);
280            return;
281        }
282    }
283}
284
285void BombsCanvas::OnChar(wxKeyEvent& event)
286{
287    int keyCode = event.GetKeyCode();
288    int prevGridFocusX = m_game->m_gridFocusX;
289    int prevGridFocusY = m_game->m_gridFocusY;
290
291    const int gridWidth = m_game->GetWidth();
292    const int gridHeight = m_game->GetHeight();
293
294    switch(keyCode)
295    {
296
297    case WXK_RIGHT:
298        m_game->m_gridFocusX++;
299        if (m_game->m_gridFocusX >= gridWidth) m_game->m_gridFocusX = 0;
300        break;
301
302    case WXK_LEFT:
303        m_game->m_gridFocusX--;
304        if (m_game->m_gridFocusX<0) m_game->m_gridFocusX = gridWidth-1;
305        break;
306
307    case WXK_DOWN:
308        m_game->m_gridFocusY++;
309        if (m_game->m_gridFocusY >= gridHeight) m_game->m_gridFocusY = 0;
310        break;
311
312    case WXK_UP:
313        m_game->m_gridFocusY--;
314        if (m_game->m_gridFocusY<0) m_game->m_gridFocusY = gridHeight-1;
315        break;
316
317    case WXK_RETURN:
318        if ( (prevGridFocusX == m_game->m_gridFocusX)
319            && (prevGridFocusY == m_game->m_gridFocusY)
320            && (m_game->IsHidden(m_game->m_gridFocusX, m_game->m_gridFocusY)) )
321        {
322            m_game->Mark(m_game->m_gridFocusX, m_game->m_gridFocusY);
323            if (!m_game->IsMarked(m_game->m_gridFocusX, m_game->m_gridFocusY))
324            {
325                Uncover(m_game->m_gridFocusX, m_game->m_gridFocusY);
326            }
327            RefreshField(m_game->m_gridFocusX, m_game->m_gridFocusY,
328                m_game->m_gridFocusX, m_game->m_gridFocusY);
329        }
330        break;
331
332    default:
333        event.Skip();
334
335    }
336
337    if ((prevGridFocusX != m_game->m_gridFocusX)
338        || (prevGridFocusY != m_game->m_gridFocusY))
339    {
340        // cause focused field to be visible after first key hit after launching new game
341        if( m_game->m_gridFocusX < 0 ) m_game->m_gridFocusX = 0;
342        if( m_game->m_gridFocusY < 0 ) m_game->m_gridFocusY = 0;
343
344        // refresh previous field and focused field
345        RefreshField(prevGridFocusX, prevGridFocusY,
346            prevGridFocusX, prevGridFocusY);
347        RefreshField(m_game->m_gridFocusX, m_game->m_gridFocusY,
348            m_game->m_gridFocusX, m_game->m_gridFocusY);
349    }
350}
351