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