1 /* -*- c++ -*-
2 FILE: MagicCube4dView.cpp
3 RCS REVISION: $Revision: 1.10 $
4 
5 COPYRIGHT: (c) 1999 -- 2003 Melinda Green, Don Hatch, and Jay Berkenbilt - Superliminal Software
6 
7 LICENSE: Free to use and modify for non-commercial purposes as long as the
8     following conditions are adhered to:
9     1) Obvious credit for the source of this code and the designs it embodies
10        are clearly made, and
11     2) Ports and derived versions of 4D Magic Cube programs are not distributed
12        without the express written permission of the authors.
13 
14 DESCRIPTION:
15     MagicCube4dView.cpp : implementation of the CMagicCube4dView class
16 */
17 
18 // COM requires that this is defined in one and only one place
19 // before d3d.h so here it is:
20 //
21 #define INITGUID
22 
23 #include "Stdafx.h"
24 #include "MagicCube4d.h"
25 
26 #include "MagicCube4dDoc.h"
27 #include "MagicCube4dView.h"
28 
29 //#include "fire.h"
30 #include "MagicCubeObj.h"
31 #include "SQuat.h"
32 
33 static double background_color[3] = { .3, .3, .3 };
34 
35 #define LOAD_COLOR(into, from, frac, white) { \
36     into.peRed   = BYTE(white * frac * from[0]); \
37     into.peGreen = BYTE(white * frac * from[1]); \
38     into.peBlue  = BYTE(white * frac * from[2]); \
39     into.peFlags = PC_NOCOLLAPSE; \
40 }
41 
42 #ifdef _DEBUG
43 #define new DEBUG_NEW
44 #undef THIS_FILE
45 static char THIS_FILE[] = __FILE__;
46 #endif
47 
48 
49 /////////////////////////////////////////////////////////////////////////////
50 // CMagicCube4dView
51 
IMPLEMENT_DYNCREATE(CMagicCube4dView,CView)52 IMPLEMENT_DYNCREATE(CMagicCube4dView, CView)
53     BEGIN_MESSAGE_MAP(CMagicCube4dView, CView)
54     //{{AFX_MSG_MAP(CMagicCube4dView)
55     ON_WM_LBUTTONDOWN()
56     ON_WM_LBUTTONUP()
57     ON_WM_MOUSEMOVE()
58     ON_WM_RBUTTONUP()
59     ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
60     ON_COMMAND(ID_SCRAMBLE_1, OnScramble1)
61     ON_COMMAND(ID_SCRAMBLE_2, OnScramble2)
62     ON_COMMAND(ID_SCRAMBLE_3, OnScramble3)
63     ON_COMMAND(ID_SCRAMBLE_4, OnScramble4)
64     ON_COMMAND(ID_SCRAMBLE_5, OnScramble5)
65     ON_COMMAND(ID_SCRAMBLE_6, OnScramble6)
66     ON_COMMAND(ID_SCRAMBLE_7, OnScramble7)
67     ON_COMMAND(ID_SCRAMBLE_8, OnScramble8)
68     ON_COMMAND(ID_SCRAMBLE_FULL, OnScrambleFull)
69     ON_COMMAND(ID_SOLVE, OnSolve)
70     ON_COMMAND(ID_RESET, OnReset)
71     ON_COMMAND(ID_NEW_PUZZLE2, OnNewPuzzle2)
72     ON_COMMAND(ID_NEW_PUZZLE3, OnNewPuzzle3)
73     ON_COMMAND(ID_NEW_PUZZLE4, OnNewPuzzle4)
74     ON_COMMAND(ID_NEW_PUZZLE5, OnNewPuzzle5)
75     ON_COMMAND(ID_FILE_SAVE, OnFileSave)
76     ON_WM_KEYDOWN()
77     ON_WM_KEYUP()
78     ON_WM_KILLFOCUS()
79     ON_COMMAND(ID_RENDERING_SOFTWAREONLY, OnRenderingSoftwareonly)
80     ON_COMMAND(ID_RENDERING_DEFAULT, OnRenderingDefault)
81     //}}AFX_MSG_MAP
82     END_MESSAGE_MAP()
83 /////////////////////////////////////////////////////////////////////////////
84 // CMagicCube4dView construction/destruction
85     static void magiccube_error_handler(const char *str, HRESULT, void *win)
86 {
87     HWND        hwnd = (HWND) win;
88     MessageBox(hwnd, str, NULL, MB_OK);
89     exit(1);
90 }
91 
CMagicCube4dView()92 CMagicCube4dView::CMagicCube4dView ()
93 {
94     // TODO: add construction code here
95 
96     m_width = m_height = 0;
97     m_d3d = NULL;
98     m_d3ddev = NULL;
99     m_d3dview = NULL;
100     m_d3drm = NULL;
101     m_scene = NULL;
102     m_objs = NULL;
103     m_camera = NULL;
104     m_lights = NULL;
105     m_dir_light = NULL;
106     m_amb_light = NULL;
107     m_clipper = NULL;
108     m_d3drmdev = NULL;
109     m_d3drmwindev = NULL;
110     m_view = NULL;
111     m_wrapd3d.SetErrorHandler(magiccube_error_handler, m_hWnd);
112     m_wrapd3d.SetFatalErrorHandler(magiccube_error_handler, m_hWnd);
113     m_nslices = 3;              // default puzzle is 3x3x3x3
114     m_puzzle = NULL;
115     m_tmp_logfile = NULL;
116     m_left_mouse_down = FALSE;
117     m_scramble_state = SCRAMBLE_NONE;
118     m_user_twists = 0;
119     m_user_twisting = 0;
120     m_user_twist_mask = 0;      // no num key down
121     ZEROVEC2(m_last_point);
122     m_software_render_only = true;
123 }
124 
~CMagicCube4dView()125 CMagicCube4dView::~CMagicCube4dView ()
126 {
127     if (m_puzzle)
128         delete      m_puzzle;
129     m_wrapd3d.Destroy();
130     RELEASE(m_d3drm);
131 }
132 
133 
Init3D(int w,int h)134 void CMagicCube4dView::Init3D(int w, int h)
135 {
136     m_update_needed = true;
137 
138     if (m_width > 0)
139     {                           // not the first time
140         m_wrapd3d.DestroyButNotDirectDraw();
141         RELEASE(m_d3drm);
142     }
143 
144     CDC        *cdc = GetDC();
145     int         bpp = cdc->GetDeviceCaps(BITSPIXEL);
146 
147     PALETTEENTRY colors[1000];
148     int         ncolors = 0;
149 
150     // HERE'S HOW TO USE THE WINDOW'S EXISTING PALETTE:
151     // CPalette *tmppal = cdc->GetCurrentPalette();
152     // int ncolors = tmppal->GetPaletteEntries(0, 1000, colors);
153 
154     // HERE'S A CUSTOM PALETTE:
155     extern const double face_colors[][3];
156     const int   nshades = 12;   // shades per color
157     BYTE        white = bpp <= 8 ? ((1 << bpp) - 1) : 255;
158     LOAD_COLOR(colors[0], face_colors[0], 0, white);
159     LOAD_COLOR(colors[1], background_color, 1, white);
160     ncolors = 2;
161     for (int c = 0; c < 8; c++)
162     {
163         for (int i = nshades; i > 0; i--)
164         {
165             double      frac = (double)i / nshades;
166             LOAD_COLOR(colors[ncolors], face_colors[c], frac, white);
167             ncolors++;
168         }
169     }
170 
171     m_width = w;
172     m_height = h;
173     m_wrapd3d.Create(m_hWnd, FALSE, w, h, bpp, colors, ncolors,
174                      m_software_render_only);
175     m_d3d = m_wrapd3d.Direct3D();
176     m_d3ddev = m_wrapd3d.Direct3DDevice();
177 
178     HRESULT     rval;
179 
180     /*
181      * Create the D3DRM object
182      */
183     rval = Direct3DRMCreate(&m_d3drm);
184     if (rval != D3DRM_OK)
185         m_wrapd3d.FatalError("Failed to create Direct3DRM.\n%s", rval);
186 
187     /*
188      * Create the master scene frame plus a frame for visible objects.
189      * Note that objects put in the root frame cannot be transformed
190      * due to a D3D bug, so the sub-frame is important.
191      */
192     rval = m_d3drm->CreateFrame(NULL, &m_scene);
193     if (rval != D3DRM_OK)
194         m_wrapd3d.FatalError("Failed to create the master scene frame.\n%s",
195                              rval);
196     // if(m_wrapd3d.CurrentMode().bitsPerPixel > 8) {
197     rval = m_scene->SetSceneBackgroundRGB(D3DVAL (background_color[0]),
198                                           D3DVAL (background_color[1]),
199                                           D3DVAL (background_color[2]));
200     if (rval != D3DRM_OK)
201         m_wrapd3d.FatalError("Failed to set background color.\n%s", rval);
202     rval = m_d3drm->CreateFrame(m_scene, &m_objs);
203     if (rval != D3DRM_OK)
204         m_wrapd3d.FatalError("Failed to create the objs frame.\n%s", rval);
205 
206     /*
207      * Setup Don's favorite initial orientation
208      */
209     rval = m_objs->AddRotation(D3DRMCOMBINE_AFTER,
210                                D3DVAL (1), D3DVAL (0), D3DVAL (0),
211                                D3DVAL (DTOR(-30)));
212     rval = m_objs->AddRotation(D3DRMCOMBINE_AFTER,
213                                D3DVAL (0), D3DVAL (1), D3DVAL (0),
214                                D3DVAL (DTOR(42)));
215 
216     /*
217      * Create the camera frame
218      */
219     rval = m_d3drm->CreateFrame(m_scene, &m_camera);
220     if (rval != D3DRM_OK)
221         m_wrapd3d.FatalError("Failed to create the camera's frame.\n%s",
222                              rval);
223     rval =
224         m_camera->SetPosition(m_scene, D3DVAL (0), D3DVAL (0), D3DVAL (-4));
225     if (rval != D3DRM_OK)
226         m_wrapd3d.
227             FatalError("Failed to position the camera in the frame.\n%s",
228                        rval);
229 
230     /*
231      * Create the lights frame, and lights
232      */
233     rval = m_d3drm->CreateFrame(m_scene, &m_lights);
234     if (rval != D3DRM_OK)
235         m_wrapd3d.FatalError("Failed to create the light's frame.\n%s", rval);
236     rval = m_lights->SetOrientation(
237         m_scene,
238         D3DVAL(-1), D3DVAL(-1), D3DVAL(2),  // direction
239         D3DVAL(0), D3DVAL(1), D3DVAL(0));   // up
240     if (rval != D3DRM_OK)
241         m_wrapd3d.FatalError("Failed to orient the lights in the frame.\n%s",
242                              rval);
243     rval = m_d3drm->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL,
244                                    D3DVAL(1), D3DVAL(1), D3DVAL(1),
245                                    &m_dir_light);
246     rval = m_lights->AddLight(m_dir_light);
247     rval = m_d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT,
248                                    D3DVAL (.2), D3DVAL (.2), D3DVAL (.2),
249                                    &m_amb_light);
250     rval = m_scene->AddLight(m_amb_light);
251 
252     rval = m_d3drm->CreateDeviceFromD3D(m_d3d, m_d3ddev, &m_d3drmdev);
253 
254     // create retained mode device and viewport
255     //
256     if (rval != DD_OK)
257         m_wrapd3d.FatalError("Failed to create device", rval);
258     if (FAILED
259         (m_d3drmdev->
260          QueryInterface(IID_IDirect3DRMWinDevice, (void **)&m_d3drmwindev)))
261         MessageBox("Failed to get win rmdevice from rmdevice");
262     rval = m_d3drm->CreateViewport(m_d3drmdev, m_camera, 0, 0,
263                                    m_d3drmdev->GetWidth(),
264                                    m_d3drmdev->GetHeight(), &m_view);
265     if (rval != D3DRM_OK)
266         m_wrapd3d.FatalError("Failed to create the viewport.\n%s", rval);
267 
268     // create puzzle object and add to scene
269     //
270     if (m_puzzle)
271     {
272         // this tmp logfile buisness is a dirty hack to
273         // allow recreation of the puzzle's state when
274         // we're simply resizing the window, because we
275         // don't really want to distroy the puzzle anyway
276         m_tmp_logfile = fopen("tmp.log", "w+");
277         if (m_tmp_logfile)
278         {
279             fprintf(m_tmp_logfile, "%s %d %ld %d\n", MAGIC_NUMBER,
280                     MAGICCUBE_FILE_VERSION, m_scramble_state, m_user_twists);
281             m_puzzle->SaveTo(m_tmp_logfile);
282             fseek(m_tmp_logfile, 0, SEEK_SET);
283             fflush(m_tmp_logfile);
284         }
285     }
286     NewPuzzle(m_nslices);
287 }
288 
289 
NewPuzzle(int ns)290 void CMagicCube4dView::NewPuzzle(int ns)
291 {
292     if (ns > MAXLENGTH)
293     {
294         MessageBox("Tried to create too large a puzzle");
295         return;
296     }
297     if (m_puzzle)
298     {
299         // m_objs->DeleteVisual(m_puzzle->GetVisual());
300         delete      m_puzzle;
301     }
302 
303     FILE       *logfp;
304     if (m_tmp_logfile)
305         logfp = m_tmp_logfile;
306     else
307         logfp = fopen("MagicCube4D.log", "r");
308     if (logfp)
309     {
310         int         file_version;
311         char        magic_number[20];   // length must be consistent with
312         // scanf format
313         magic_number[19] = '\0';
314         fscanf(logfp, "%19s %d%ld%d", magic_number, &file_version,
315                &m_scramble_state, &m_user_twists);
316         if (!((file_version == MAGICCUBE_FILE_VERSION) &&
317               (strcmp(magic_number, MAGIC_NUMBER) == 0)))
318         {
319             MessageBox("Bad MagicCube4D.log file or old version.");
320             fclose(logfp);
321             logfp = NULL;
322         }
323     }
324     FILE       *colorsfp = fopen("MagicCube4D.rgb", "r");
325     double      colors[9][3];
326     if (colorsfp)
327     {
328         for (int i = 0; i < 9; i++)
329             if (3 != fscanf(colorsfp, "%lf%lf%lf",
330                             &colors[i][0], &colors[i][1], &colors[i][2]))
331             {
332                 colorsfp = NULL;
333                 break;
334             }
335     }
336     m_puzzle = new MagicCubeObject (m_nslices, logfp,
337                                     colorsfp ? colors : NULL, m_d3drmdev,
338                                     m_d3drm);
339     if (colorsfp)
340     {
341         SET3(background_color, colors[8]);
342         fclose(colorsfp);
343     }
344     if (logfp)
345         fclose(logfp);
346     if (m_tmp_logfile)
347         unlink("tmp.log");
348 
349     if (!m_puzzle)
350         m_wrapd3d.FatalError("Failed to create the MagicCubeObject.\n", 0);
351     else if (!m_puzzle->IsValid())
352         m_wrapd3d.
353             FatalError("Couldn't init MagicCubeObject. Not enough memory?\n", 0);
354 
355 /* This mesh casts shadows just fine */
356 #if 0
357     HRESULT     res;
358     LPDIRECT3DRMMESHBUILDER teapot_builder = NULL;
359     LPDIRECT3DRMMESH teapot_mesh = NULL;
360     res = m_d3drm->CreateMeshBuilder(&teapot_builder);
361     res =
362         teapot_builder->Load("tpot0.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL);
363     res = teapot_builder->CreateMesh(&teapot_mesh);
364     m_objs->AddVisual(teapot_mesh);
365 #endif
366 
367     m_objs->AddVisual(m_puzzle->GetVisual());
368 
369 /* User Visuals, disappear when casting shadows */
370 #if 0
371     IDirect3DRMVisual *shadow;
372     res = m_d3drm->CreateShadow(m_puzzle->GetVisual(), m_dir_light,
373                                 D3DVAL (0), D3DVAL (-1), D3DVAL (0),
374                                 D3DVAL (0), D3DVAL (1), D3DVAL (0), &shadow);
375     // res = m_shadow_frame->AddVisual(shadow);
376     res = m_objs->AddVisual(shadow);
377 #endif
378 }
379 
380 
381 /////////////////////////////////////////////////////////////////////////////
382 // CMagicCube4dView drawing
383 
InitDrawing()384 void CMagicCube4dView::InitDrawing()
385 {
386     CRect       r;
387     GetClientRect(r);
388     if (m_width != r.Width() || m_height != r.Height())
389         Init3D(r.Width(), r.Height());
390     m_update_needed = true;
391     Update3d();
392 }
393 
394 
OnDraw(CDC * pDC)395 void CMagicCube4dView::OnDraw(CDC *pDC)
396 {
397     CMagicCube4dDoc *pDoc = GetDocument();
398     ASSERT_VALID(pDoc);
399 
400     // TODO: add draw code for native data here
401     /*
402        CPaintDC dc(this); // device context for painting if(m_d3drmwindev)
403        m_d3drmwindev->HandlePaint(dc); */
404 
405     InitDrawing();
406 }
407 
408 
Update3d()409 BOOL CMagicCube4dView::Update3d()
410 {
411     HRESULT     rval;
412 
413     if (!(m_update_needed ||
414           m_user_twisting ||
415           m_left_mouse_down || (m_puzzle && m_puzzle->IsAnimating())))
416     {
417         return m_puzzle && m_puzzle->IsAnimating();
418     }
419     m_update_needed = false;
420 
421     // Useful debugging statement toggles color each frame
422 #if 0
423     static int  cnt = 0;
424     rval =
425         m_scene->SetSceneBackgroundRGB(cnt % 2 ==
426                                        0 ? 0 : D3DVAL (background_color[0]),
427                                        cnt % 2 ==
428                                        0 ? 0 : D3DVAL (background_color[1]),
429                                        cnt % 2 ==
430                                        0 ? 0 : D3DVAL (background_color[2]));
431     if (rval != D3DRM_OK)
432         m_wrapd3d.FatalError("Failed to set background color.\n%s", rval);
433     cnt++;
434 #endif
435 
436     // Force a D3D redraw and get the resulting image into the
437     // front buffer.
438     //
439     rval = m_view->Clear();
440     rval = m_view->Render(m_scene);
441     rval = m_d3drmdev->Update();
442     // Flip or blt
443     if (m_wrapd3d.IsFullScreen())
444     {
445         m_wrapd3d.Flip();
446     }
447     else
448     {
449         CRect       clientRect;
450         CRect       screenRect; // destination rectangle in front buffer
451 
452         // ??? be smarter about what to update
453         // ??? make sure back buffer is updated correctly
454         GetClientRect(&clientRect);
455         m_view->ForceUpdate(0, 0, clientRect.Width(), clientRect.Height());
456 
457         screenRect = clientRect;
458         ClientToScreen(screenRect);
459 
460         // ??? be smarter about what to update
461         m_wrapd3d.BltBackBuffer(screenRect, clientRect);
462     }
463 
464     // Check to see if a user initiated twist solved a scramble.
465     // If so, trigger a reward and reset m_scramble_state.
466     // Also turns off m_user_twisting when an animation finishes.
467     //
468     //
469     if (m_user_twisting)
470     {
471         if (m_puzzle)
472         {
473             if (!m_puzzle->IsAnimating())
474             {                   // finished twisting
475                 m_user_twisting = 0;
476                 if (m_puzzle->IsSolved())
477                 {
478                     switch (m_scramble_state)
479                     {
480                     case SCRAMBLE_PARTIAL:
481                         MessageBeep(MB_ICONASTERISK);
482                         break;
483                     case SCRAMBLE_FULL:
484                         // this should really be a BIG reward
485                         MessageBeep(MB_ICONASTERISK);
486                         break;
487                     }
488                     m_scramble_state = SCRAMBLE_NONE;
489                     // Note: history should still contain moves
490                 }
491             }
492         }
493         else                    // should never happen
494             m_user_twisting = 0;
495     }
496 
497 
498     return m_puzzle && m_puzzle->IsAnimating();
499 }
500 
501 /////////////////////////////////////////////////////////////////////////////
502 // CMagicCube4dView diagnostics
503 
504 #ifdef _DEBUG
AssertValid() const505 void CMagicCube4dView::AssertValid() const
506 {
507     CView::AssertValid();
508 }
509 
Dump(CDumpContext & dc) const510 void CMagicCube4dView::Dump(CDumpContext & dc) const
511 {
512     CView::Dump(dc);
513 }
514 
GetDocument()515 CMagicCube4dDoc *CMagicCube4dView::GetDocument() // non-debug version is inline
516 {
517     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMagicCube4dDoc)));
518     return (CMagicCube4dDoc *)m_pDocument;
519 }
520 #endif // _DEBUG
521 
522 /////////////////////////////////////////////////////////////////////////////
523 // CMagicCube4dView message handlers
524 
525 
526 
OnActivateView(BOOL bActivate,CView * pActivateView,CView * pDeactiveView)527 void CMagicCube4dView::OnActivateView(BOOL bActivate, CView * pActivateView,
528                                       CView * pDeactiveView)
529 {
530     if (m_d3drmwindev)
531         m_d3drmwindev->HandleActivate(bActivate);
532 
533     CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
534 }
535 
OnLButtonDown(UINT nFlags,CPoint point)536 void CMagicCube4dView::OnLButtonDown(UINT nFlags, CPoint point)
537 {
538     m_left_mouse_down = TRUE;
539     m_left_dragged = FALSE;
540     m_last_point[0] = point.x;
541     m_last_point[1] = point.y;
542     SetCapture();
543 
544     CView::OnLButtonDown(nFlags, point);
545 }
546 
547 
DoPick(int x,int y,int dir,int rotate)548 void CMagicCube4dView::DoPick(int x, int y, int dir, int rotate)
549 {
550     int         block, tri;
551     int         hit = m_puzzle->Pick(m_view, x, y, &block, &tri);
552     if (hit)
553     {
554         int         success;
555         if (rotate)
556             success = m_puzzle->RotateFaceToCenter(block);
557         else                    // twist top slice by default unless num keys
558                                 // pressed
559             success = m_puzzle->TwistAbout(block, dir,
560                                            m_user_twist_mask ?
561                                            m_user_twist_mask : 1);
562         if (success)
563         {
564             m_user_twists++;
565             m_user_twisting = 1;
566             // GetDocument()->SetModifiedFlag(TRUE);
567         }
568         else                    // can't twist or rotate that
569             MessageBeep(MB_ICONEXCLAMATION);
570     }
571     else                        // missed
572         MessageBeep(MB_ICONEXCLAMATION);
573 }
574 
575 
OnLButtonUp(UINT nFlags,CPoint point)576 void CMagicCube4dView::OnLButtonUp(UINT nFlags, CPoint point)
577 {
578     if (!m_left_dragged)        // no motion means do a pick
579         DoPick(point.x, point.y, -1, nFlags & MK_CONTROL);
580     ReleaseCapture();
581     m_left_mouse_down = FALSE;
582     m_left_dragged = FALSE;
583 
584     CView::OnLButtonUp(nFlags, point);
585 }
586 
587 
588 
OnRButtonUp(UINT nFlags,CPoint point)589 void CMagicCube4dView::OnRButtonUp(UINT nFlags, CPoint point)
590 {
591     DoPick(point.x, point.y, 1, nFlags & MK_CONTROL);
592 
593     CView::OnRButtonUp(nFlags, point);
594 }
595 
596 
597 #define NORMALIZE_VEC2(dst, src) { \
598     double len = sqrt(NORMSQRD2(src)); \
599     VDS2(dst, src, len); }
600 #define NORMALIZE_VEC3(dst, src) { \
601     double len = sqrt(NORMSQRD3(src)); \
602     VDS3(dst, src, len); }
603 
OnMouseMove(UINT nFlags,CPoint point)604 void CMagicCube4dView::OnMouseMove(UINT nFlags, CPoint point)
605 {
606     if (TRUE == m_left_mouse_down)
607     {
608         m_left_dragged = TRUE;
609         double      end[2], drag_dir[2];
610         Vector3     axis;
611         end[0] = point.x;
612         end[1] = point.y;
613         VMV2(drag_dir, m_last_point, end);
614         drag_dir[1] *= -1;      // in Windows, Y is down, so invert it
615         XV2(axis, drag_dir);
616         double      len = sqrt(NORMSQRD2(axis));
617         if (len > .0001)
618         {                       // do nothing if ended where we started
619             VDS2(axis, axis, len);
620             axis[2] = 0;
621             double      rads = len / 200.0;
622             Matrix3     mat(SQuat (axis, rads));
623             D3DRMMATRIX4D d3dmat;
624             SETMAT4from3(d3dmat, mat, 0, 1);
625             m_objs->AddTransform(D3DRMCOMBINE_AFTER, d3dmat);
626         }
627         m_last_point[0] = point.x;
628         m_last_point[1] = point.y;
629     }
630 
631     CView::OnMouseMove(nFlags, point);
632 }
633 
634 
OnEditUndo()635 void CMagicCube4dView::OnEditUndo()
636 {
637     if( ! m_puzzle->Undo(m_shift_down)) { // FIX: shiftmask not working
638         MessageBeep(MB_ICONASTERISK);
639         return;
640     }
641     if (0 == m_user_twists)
642         m_scramble_state = SCRAMBLE_NONE;   // scramble now doesn't count
643     else
644         m_user_twists--;
645 }
646 
OnScramble1()647 void CMagicCube4dView::OnScramble1()
648 {
649     Scramble(1);
650 }
OnScramble2()651 void CMagicCube4dView::OnScramble2()
652 {
653     Scramble(2);
654 }
OnScramble3()655 void CMagicCube4dView::OnScramble3()
656 {
657     Scramble(3);
658 }
OnScramble4()659 void CMagicCube4dView::OnScramble4()
660 {
661     Scramble(4);
662 }
OnScramble5()663 void CMagicCube4dView::OnScramble5()
664 {
665     Scramble(5);
666 }
OnScramble6()667 void CMagicCube4dView::OnScramble6()
668 {
669     Scramble(6);
670 }
OnScramble7()671 void CMagicCube4dView::OnScramble7()
672 {
673     Scramble(7);
674 }
OnScramble8()675 void CMagicCube4dView::OnScramble8()
676 {
677     Scramble(8);
678 }
OnScrambleFull()679 void CMagicCube4dView::OnScrambleFull()
680 {
681     Scramble(NSCRAMBLECHEN);
682 }
683 
Scramble(int scrambulations)684 void CMagicCube4dView::Scramble(int scrambulations)
685 {
686     if (!m_puzzle)
687         return;
688     m_puzzle->Scramble(scrambulations);
689     if (scrambulations < NSCRAMBLECHEN)
690         m_scramble_state = SCRAMBLE_PARTIAL;    // he's practicing
691     else
692         m_scramble_state = SCRAMBLE_FULL;   // he's going for it!
693     m_update_needed = true;
694     m_user_twists = 0;          // there are no user twists after the last
695                                 // scramble (i.e. this one)
696 }
697 
OnSolve()698 void CMagicCube4dView::OnSolve()
699 {
700     m_puzzle->Cheat();
701     m_scramble_state = SCRAMBLE_NONE;
702     m_user_twists = 0;
703 }
704 
OnReset()705 void CMagicCube4dView::OnReset()
706 {
707     m_puzzle->Reset();
708     m_scramble_state = SCRAMBLE_NONE;
709     m_user_twists = 0;
710     m_user_twisting = 0;        // in case user resets during a twist
711     m_update_needed = true;
712 }
713 
OnNewPuzzle2()714 void CMagicCube4dView::OnNewPuzzle2()
715 {
716     NewPuzzle(2);
717 }
OnNewPuzzle3()718 void CMagicCube4dView::OnNewPuzzle3()
719 {
720     NewPuzzle(3);
721 }
OnNewPuzzle4()722 void CMagicCube4dView::OnNewPuzzle4()
723 {
724     NewPuzzle(4);
725 }
OnNewPuzzle5()726 void CMagicCube4dView::OnNewPuzzle5()
727 {
728     NewPuzzle(5);
729 }
730 
OnFileSave()731 void CMagicCube4dView::OnFileSave()
732 {
733     if (m_puzzle)
734     {
735         FILE       *fp;
736         if ((fp = fopen("MagicCube4D.log", "w")) == NULL)
737             MessageBox("Can't save to MagicCube4D.log, sorry.");
738         else
739         {
740             fprintf(fp, "%s %d %ld %d\n", MAGIC_NUMBER,
741                     MAGICCUBE_FILE_VERSION, m_scramble_state, m_user_twists);
742             m_puzzle->SaveTo(fp);
743             fclose(fp);
744         }
745     }
746     else
747         MessageBox("No puzzle to save.");
748 }
749 
750 
751 // message handlers for key presses.
752 // currently, all they look for are number keys that
753 // desnigate slice planes to twist.
754 
OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)755 void CMagicCube4dView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
756 {
757     if ('0' < nChar && nChar <= '9')
758     {
759         m_user_twist_mask |= 1 << (nChar - '0' - 1);
760         SetFocus();
761     }
762     m_shift_down = nChar == 16;
763     CView::OnKeyDown(nChar, nRepCnt, nFlags);
764 }
765 
OnKeyUp(UINT nChar,UINT nRepCnt,UINT nFlags)766 void CMagicCube4dView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
767 {
768     if ('0' < nChar && nChar <= '9')
769     {
770         m_user_twist_mask &= ~(1 << (nChar - '0' - 1));
771     }
772     m_shift_down = FALSE;
773 
774     CView::OnKeyUp(nChar, nRepCnt, nFlags);
775 }
776 
OnKillFocus(CWnd * pNewWnd)777 void CMagicCube4dView::OnKillFocus(CWnd * pNewWnd)
778 {
779     CView::OnKillFocus(pNewWnd);
780 
781     m_user_twist_mask = 0;      // in case we miss a keyup event
782 }
783 
784 
785 
OnRenderingSoftwareonly()786 void CMagicCube4dView::OnRenderingSoftwareonly()
787 {
788     m_software_render_only = true;
789     InitDrawing();
790 }
791 
OnRenderingDefault()792 void CMagicCube4dView::OnRenderingDefault()
793 {
794     m_software_render_only = false;
795     InitDrawing();
796 }
797 
798 // Local Variables:
799 // c-basic-offset: 4
800 // c-comment-only-line-offset: 0
801 // c-file-offsets: ((defun-block-intro . +) (block-open . 0) (substatement-open . 0) (statement-cont . +) (statement-case-open . +4) (arglist-intro . +) (arglist-close . +) (inline-open . 0))
802 // indent-tabs-mode: nil
803 // End:
804